/* xrectsel.c -- print the geometry of a rectangular screen region.
|
|
|
|
Copyright (C) 2011-2014 lolilolicon <lolilolicon@gmail.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/cursorfont.h>
|
|
|
|
#define die(args...) do {error(args); exit(EXIT_FAILURE); } while(0)
|
|
|
|
typedef struct Region Region;
|
|
struct Region {
|
|
Window root;
|
|
int x; /* offset from left of screen */
|
|
int y; /* offset from top of screen */
|
|
int X; /* offset from right of screen */
|
|
int Y; /* offset from bottom of screen */
|
|
unsigned int w; /* width */
|
|
unsigned int h; /* height */
|
|
unsigned int b; /* border_width */
|
|
unsigned int d; /* depth */
|
|
};
|
|
|
|
static void error(const char *errstr, ...);
|
|
static int print_region_attr(const char *fmt, Region region);
|
|
static int select_region(Display *dpy, Window root, Region *region);
|
|
|
|
int main(int argc, const char *argv[])
|
|
{
|
|
Display *dpy;
|
|
Window root;
|
|
Region sr; /* selected region */
|
|
const char *fmt; /* format string */
|
|
|
|
dpy = XOpenDisplay(NULL);
|
|
if (!dpy) {
|
|
die("failed to open display %s\n", getenv("DISPLAY"));
|
|
}
|
|
|
|
root = DefaultRootWindow(dpy);
|
|
|
|
fmt = argc > 1 ? argv[1] : "%wx%h+%x+%y\n";
|
|
|
|
/* interactively select a rectangular region */
|
|
if (select_region(dpy, root, &sr) != EXIT_SUCCESS) {
|
|
XCloseDisplay(dpy);
|
|
die("failed to select a rectangular region\n");
|
|
}
|
|
|
|
print_region_attr(fmt, sr);
|
|
|
|
XCloseDisplay(dpy);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static void error(const char *errstr, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
fprintf(stderr, "xrectsel: ");
|
|
va_start(ap, errstr);
|
|
vfprintf(stderr, errstr, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static int print_region_attr(const char *fmt, Region region)
|
|
{
|
|
const char *s;
|
|
|
|
for (s = fmt; *s; ++s) {
|
|
if (*s == '%') {
|
|
switch (*++s) {
|
|
case '%':
|
|
putchar('%');
|
|
break;
|
|
case 'x':
|
|
printf("%i", region.x);
|
|
break;
|
|
case 'y':
|
|
printf("%i", region.y);
|
|
break;
|
|
case 'X':
|
|
printf("%i", region.X);
|
|
break;
|
|
case 'Y':
|
|
printf("%i", region.Y);
|
|
break;
|
|
case 'w':
|
|
printf("%u", region.w);
|
|
break;
|
|
case 'h':
|
|
printf("%u", region.h);
|
|
break;
|
|
case 'b':
|
|
printf("%u", region.b);
|
|
break;
|
|
case 'd':
|
|
printf("%u", region.d);
|
|
break;
|
|
}
|
|
} else {
|
|
putchar(*s);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int select_region(Display *dpy, Window root, Region *region)
|
|
{
|
|
XEvent ev;
|
|
|
|
GC sel_gc;
|
|
XGCValues sel_gv;
|
|
|
|
int done = 0, btn_pressed = 0;
|
|
int x = 0, y = 0;
|
|
unsigned int width = 0, height = 0;
|
|
int start_x = 0, start_y = 0;
|
|
|
|
Cursor cursor;
|
|
cursor = XCreateFontCursor(dpy, XC_crosshair);
|
|
|
|
/* Grab pointer for these events */
|
|
XGrabPointer(dpy, root, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
|
|
GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
|
|
|
|
sel_gv.function = GXinvert;
|
|
sel_gv.subwindow_mode = IncludeInferiors;
|
|
sel_gv.line_width = 1;
|
|
sel_gc = XCreateGC(dpy, root, GCFunction | GCSubwindowMode | GCLineWidth, &sel_gv);
|
|
|
|
for (;;) {
|
|
XNextEvent(dpy, &ev);
|
|
switch (ev.type) {
|
|
case ButtonPress:
|
|
btn_pressed = 1;
|
|
x = start_x = ev.xbutton.x_root;
|
|
y = start_y = ev.xbutton.y_root;
|
|
width = height = 0;
|
|
break;
|
|
case MotionNotify:
|
|
/* Draw only if button is pressed */
|
|
if (btn_pressed) {
|
|
/* Re-draw last Rectangle to clear it */
|
|
XDrawRectangle(dpy, root, sel_gc, x, y, width, height);
|
|
|
|
x = ev.xbutton.x_root;
|
|
y = ev.xbutton.y_root;
|
|
|
|
if (x > start_x) {
|
|
width = x - start_x;
|
|
x = start_x;
|
|
} else {
|
|
width = start_x - x;
|
|
}
|
|
if (y > start_y) {
|
|
height = y - start_y;
|
|
y = start_y;
|
|
} else {
|
|
height = start_y - y;
|
|
}
|
|
|
|
/* Draw Rectangle */
|
|
XDrawRectangle(dpy, root, sel_gc, x, y, width, height);
|
|
XFlush(dpy);
|
|
}
|
|
break;
|
|
case ButtonRelease:
|
|
done = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (done)
|
|
break;
|
|
}
|
|
|
|
/* Re-draw last Rectangle to clear it */
|
|
XDrawRectangle(dpy, root, sel_gc, x, y, width, height);
|
|
XFlush(dpy);
|
|
|
|
XUngrabPointer(dpy, CurrentTime);
|
|
XFreeCursor(dpy, cursor);
|
|
XFreeGC(dpy, sel_gc);
|
|
XSync(dpy, 1);
|
|
|
|
Region rr; /* root region */
|
|
Region sr; /* selected region */
|
|
|
|
if (False == XGetGeometry(dpy, root, &rr.root, &rr.x, &rr.y, &rr.w, &rr.h, &rr.b, &rr.d)) {
|
|
error("failed to get root window geometry\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
sr.x = x;
|
|
sr.y = y;
|
|
sr.w = width;
|
|
sr.h = height;
|
|
/* calculate right and bottom offset */
|
|
sr.X = rr.w - sr.x - sr.w;
|
|
sr.Y = rr.h - sr.y - sr.h;
|
|
/* those doesn't really make sense but should be set */
|
|
sr.b = rr.b;
|
|
sr.d = rr.d;
|
|
*region = sr;
|
|
return EXIT_SUCCESS;
|
|
}
|