xgetshape

annotate src/main.c @ 2:b832d3b3ed98

taking offset into account in get_window_shape
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 01 Feb 2016 12:18:29 +0200
parents 9b560415bad4
children
rev   line source
nuclear@0 1 #include <stdio.h>
nuclear@0 2 #include <stdlib.h>
nuclear@0 3 #include <X11/Xlib.h>
nuclear@1 4 #include <X11/cursorfont.h>
nuclear@0 5 #include <X11/extensions/shape.h>
nuclear@0 6 #include <GL/glx.h>
nuclear@0 7 #include "image.h"
nuclear@0 8 #include "texture.h"
nuclear@0 9
nuclear@0 10 void redraw(void);
nuclear@0 11 void reshape(int x, int y);
nuclear@0 12 int handle_event(XEvent *ev);
nuclear@0 13
nuclear@0 14 /* X helper functions */
nuclear@0 15 Window create_window(int xsz, int ysz);
nuclear@0 16 void set_window_title(Window win, const char *title);
nuclear@0 17 int get_window_shape(Window win, struct image *img);
nuclear@1 18 XID get_window_id(Display *dpy, int screen, int button);
nuclear@0 19
nuclear@0 20 Display *dpy;
nuclear@0 21 Window win;
nuclear@0 22 GLXContext ctx;
nuclear@0 23 int width, height;
nuclear@0 24 int mapped;
nuclear@0 25 int redisp_pending;
nuclear@0 26 Atom xa_wm_prot, xa_wm_del_win;
nuclear@0 27 struct image img;
nuclear@0 28 struct texture tex;
nuclear@0 29 struct texture chess_tex;
nuclear@0 30 float aspect;
nuclear@1 31 int sel_active;
nuclear@1 32 Cursor sel_cursor;
nuclear@0 33
nuclear@0 34 unsigned int target_xid = 0;
nuclear@0 35
nuclear@0 36 int main(int argc, char **argv)
nuclear@0 37 {
nuclear@0 38 int event_base, error_base;
nuclear@0 39 char *endp;
nuclear@0 40 struct image chess_img;
nuclear@0 41
nuclear@1 42 if(argv[1] && !(target_xid = strtol(argv[1], &endp, 0))) {
nuclear@0 43 fprintf(stderr, "invalid argument: %s\n", argv[1]);
nuclear@0 44 return 1;
nuclear@0 45 }
nuclear@0 46
nuclear@0 47 if(!(dpy = XOpenDisplay(0))) {
nuclear@0 48 fprintf(stderr, "failed to open display\n");
nuclear@0 49 return 1;
nuclear@0 50 }
nuclear@0 51 xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
nuclear@0 52 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
nuclear@0 53
nuclear@1 54 sel_cursor = XCreateFontCursor(dpy, XC_crosshair);
nuclear@1 55
nuclear@0 56 if(!XShapeQueryExtension(dpy, &event_base, &error_base)) {
nuclear@0 57 fprintf(stderr, "shape extension unsupported by this X server\n");
nuclear@0 58 XCloseDisplay(dpy);
nuclear@0 59 return 1;
nuclear@0 60 }
nuclear@1 61
nuclear@1 62 if(!target_xid) {
nuclear@1 63 target_xid = get_window_id(dpy, 0, Button1);
nuclear@1 64 }
nuclear@1 65 if(target_xid) {
nuclear@1 66 image_destroy(&img);
nuclear@1 67 if(get_window_shape(target_xid, &img) == -1) {
nuclear@1 68 destroy_texture(&tex);
nuclear@1 69 }
nuclear@1 70 }
nuclear@1 71
nuclear@1 72 if(!(win = create_window(800 * (float)img.width / (float)img.height, 800))) {
nuclear@0 73 return 1;
nuclear@0 74 }
nuclear@1 75 if(img.pixels) {
nuclear@1 76 image_texture(&tex, &img);
nuclear@0 77 }
nuclear@0 78
nuclear@0 79 image_create(&chess_img, 256, 256);
nuclear@1 80 image_chess(&chess_img, 8, 192, 100, 64, 64, 100, 192);
nuclear@0 81 image_texture(&chess_tex, &chess_img);
nuclear@0 82 image_destroy(&chess_img);
nuclear@0 83
nuclear@0 84 for(;;) {
nuclear@0 85 XEvent ev;
nuclear@0 86
nuclear@1 87 do {
nuclear@1 88 XNextEvent(dpy, &ev);
nuclear@1 89 if(handle_event(&ev) == -1) {
nuclear@1 90 goto quit;
nuclear@1 91 }
nuclear@1 92 } while(XPending(dpy));
nuclear@1 93
nuclear@1 94 if(redisp_pending) {
nuclear@1 95 redisp_pending = 0;
nuclear@1 96 redraw();
nuclear@0 97 }
nuclear@0 98 }
nuclear@0 99
nuclear@1 100 quit:
nuclear@0 101 XDestroyWindow(dpy, win);
nuclear@0 102 XCloseDisplay(dpy);
nuclear@0 103 return 0;
nuclear@0 104 }
nuclear@0 105
nuclear@0 106 void redraw(void)
nuclear@0 107 {
nuclear@0 108 glClearColor(0.4, 0.4, 0.4, 1);
nuclear@0 109 glClear(GL_COLOR_BUFFER_BIT);
nuclear@0 110
nuclear@0 111 glMatrixMode(GL_MODELVIEW);
nuclear@0 112 glLoadIdentity();
nuclear@0 113
nuclear@0 114 glEnable(GL_TEXTURE_2D);
nuclear@0 115 glBindTexture(GL_TEXTURE_2D, chess_tex.id);
nuclear@0 116
nuclear@0 117
nuclear@0 118 /* background */
nuclear@0 119 glBegin(GL_QUADS);
nuclear@1 120 glColor3f(1, 1, 1);
nuclear@0 121 glTexCoord2f(0, 0); glVertex2f(-aspect, -1);
nuclear@1 122 glTexCoord2f(aspect, 0); glVertex2f(aspect, -1);
nuclear@1 123 glTexCoord2f(aspect, 1); glVertex2f(aspect, 1);
nuclear@0 124 glTexCoord2f(0, 1); glVertex2f(-aspect, 1);
nuclear@0 125 glEnd();
nuclear@0 126
nuclear@1 127 if(tex.id) {
nuclear@1 128 int i, ssamples = 64;
nuclear@1 129 float ssample_weight = 1.0 / (float)ssamples;
nuclear@1 130 float shadow_spread = 0.05;
nuclear@1 131 float img_aspect = (float)tex.width / (float)tex.height;
nuclear@0 132
nuclear@1 133 /* draw the window shape */
nuclear@1 134 glEnable(GL_BLEND);
nuclear@1 135 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
nuclear@0 136
nuclear@1 137 glBindTexture(GL_TEXTURE_2D, tex.id);
nuclear@1 138 glMatrixMode(GL_TEXTURE);
nuclear@1 139 glLoadIdentity();
nuclear@1 140 glScalef((float)tex.width / (float)tex.tex_width,
nuclear@1 141 (float)tex.height / (float)tex.tex_height,
nuclear@1 142 1.0);
nuclear@0 143
nuclear@1 144 glMatrixMode(GL_MODELVIEW);
nuclear@1 145 glPushMatrix();
nuclear@1 146 glScalef(0.9 * img_aspect, 0.9, 0.9);
nuclear@0 147
nuclear@1 148 /* shadow */
nuclear@1 149 for(i=0; i<ssamples; i++) {
nuclear@1 150 float xoffs = shadow_spread * ((float)rand() / (float)RAND_MAX - 0.5f);
nuclear@1 151 float yoffs = shadow_spread * ((float)rand() / (float)RAND_MAX - 0.5f);
nuclear@0 152
nuclear@1 153 glPushMatrix();
nuclear@1 154 glTranslatef(0.075 + xoffs, -0.075 + yoffs, 0);
nuclear@1 155 glBegin(GL_QUADS);
nuclear@1 156 glColor4f(0, 0, 0, ssample_weight);
nuclear@1 157 glTexCoord2f(0, 1); glVertex2f(-1, -1);
nuclear@1 158 glTexCoord2f(1, 1); glVertex2f(1, -1);
nuclear@1 159 glTexCoord2f(1, 0); glVertex2f(1, 1);
nuclear@1 160 glTexCoord2f(0, 0); glVertex2f(-1, 1);
nuclear@1 161 glEnd();
nuclear@1 162 glPopMatrix();
nuclear@1 163 }
nuclear@0 164
nuclear@1 165 /* window */
nuclear@1 166 glBegin(GL_QUADS);
nuclear@1 167 glColor3f(1, 1, 0);
nuclear@1 168 glTexCoord2f(0, 1); glVertex2f(-1, -1);
nuclear@1 169 glTexCoord2f(1, 1); glVertex2f(1, -1);
nuclear@1 170 glTexCoord2f(1, 0); glVertex2f(1, 1);
nuclear@1 171 glTexCoord2f(0, 0); glVertex2f(-1, 1);
nuclear@1 172 glEnd();
nuclear@1 173
nuclear@1 174 glMatrixMode(GL_TEXTURE);
nuclear@1 175 glLoadIdentity();
nuclear@1 176 glMatrixMode(GL_MODELVIEW);
nuclear@1 177 glPopMatrix();
nuclear@1 178
nuclear@1 179 glDisable(GL_BLEND);
nuclear@1 180 }
nuclear@0 181
nuclear@0 182 glXSwapBuffers(dpy, win);
nuclear@0 183 }
nuclear@0 184
nuclear@0 185 void reshape(int x, int y)
nuclear@0 186 {
nuclear@0 187 aspect = (float)x / (float)y;
nuclear@0 188 glViewport(0, 0, x, y);
nuclear@0 189
nuclear@0 190 glMatrixMode(GL_PROJECTION);
nuclear@0 191 glLoadIdentity();
nuclear@0 192 glScalef(1.0 / aspect, 1.0, 1.0);
nuclear@0 193 }
nuclear@0 194
nuclear@0 195 int handle_event(XEvent *ev)
nuclear@0 196 {
nuclear@0 197 switch(ev->type) {
nuclear@0 198 case MapNotify:
nuclear@0 199 case UnmapNotify:
nuclear@0 200 mapped = ev->type == MapNotify ? 1 : 0;
nuclear@0 201 break;
nuclear@0 202
nuclear@0 203 case Expose:
nuclear@1 204 redisp_pending = 1;
nuclear@0 205 break;
nuclear@0 206
nuclear@0 207 case KeyPress:
nuclear@0 208 {
nuclear@0 209 KeySym sym = XLookupKeysym(&ev->xkey, 0);
nuclear@0 210
nuclear@0 211 switch(sym) {
nuclear@0 212 case XK_Escape:
nuclear@0 213 return -1;
nuclear@1 214
nuclear@1 215 case ' ':
nuclear@1 216 target_xid = get_window_id(dpy, 0, Button1);
nuclear@1 217 image_destroy(&img);
nuclear@1 218 if(get_window_shape(target_xid, &img) == -1) {
nuclear@1 219 destroy_texture(&tex);
nuclear@1 220 } else {
nuclear@1 221 image_texture(&tex, &img);
nuclear@1 222 redisp_pending = 1;
nuclear@1 223 }
nuclear@1 224 break;
nuclear@0 225 }
nuclear@0 226 }
nuclear@0 227 break;
nuclear@0 228
nuclear@0 229 case ConfigureNotify:
nuclear@0 230 if(ev->xconfigure.width != width || ev->xconfigure.height != height) {
nuclear@0 231 width = ev->xconfigure.width;
nuclear@0 232 height = ev->xconfigure.height;
nuclear@0 233 reshape(width, height);
nuclear@0 234 }
nuclear@0 235 break;
nuclear@0 236
nuclear@0 237 case ClientMessage:
nuclear@0 238 if(ev->xclient.message_type == xa_wm_prot) {
nuclear@0 239 if(ev->xclient.data.l[0] == xa_wm_del_win) {
nuclear@0 240 return -1;
nuclear@0 241 }
nuclear@0 242 }
nuclear@0 243 break;
nuclear@1 244
nuclear@1 245 case ButtonPress:
nuclear@1 246 /*if(sel_active && ev->xbutton.button == Button1) {
nuclear@1 247 target_xid = ev->xbutton.subwindow;
nuclear@1 248 printf("clicked on window: %x\n", target_xid);
nuclear@1 249
nuclear@1 250 image_destroy(&img);
nuclear@1 251 if(get_window_shape(target_xid, &img) == -1) {
nuclear@1 252 destroy_texture(&tex);
nuclear@1 253 } else {
nuclear@1 254 image_texture(&tex, &img);
nuclear@1 255 }
nuclear@1 256 }*/
nuclear@1 257 break;
nuclear@0 258 }
nuclear@0 259 return 0;
nuclear@0 260 }
nuclear@0 261
nuclear@0 262 Window create_window(int xsz, int ysz)
nuclear@0 263 {
nuclear@0 264 Window w, root;
nuclear@0 265 XVisualInfo *vis;
nuclear@0 266 XClassHint chint;
nuclear@0 267 XSetWindowAttributes xattr;
nuclear@0 268 unsigned int evmask, xattr_mask;
nuclear@0 269 int scr;
nuclear@0 270 int glxattr[] = {
nuclear@0 271 GLX_RGBA, GLX_DOUBLEBUFFER,
nuclear@0 272 GLX_RED_SIZE, 8,
nuclear@0 273 GLX_GREEN_SIZE, 8,
nuclear@0 274 GLX_BLUE_SIZE, 8,
nuclear@0 275 GLX_DEPTH_SIZE, 24,
nuclear@0 276 GLX_USE_GL, 1,
nuclear@0 277 None
nuclear@0 278 };
nuclear@0 279
nuclear@0 280 scr = DefaultScreen(dpy);
nuclear@0 281 root = RootWindow(dpy, scr);
nuclear@0 282
nuclear@0 283 if(!(vis = glXChooseVisual(dpy, scr, glxattr))) {
nuclear@0 284 printf("failed to find a suitable visual\n");
nuclear@0 285 return 0;
nuclear@0 286 }
nuclear@0 287 if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
nuclear@0 288 XFree(vis);
nuclear@0 289 return 0;
nuclear@0 290 }
nuclear@0 291
nuclear@0 292 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
nuclear@0 293 xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
nuclear@0 294 xattr_mask = CWColormap | CWBackPixel | CWBorderPixel;
nuclear@0 295
nuclear@0 296 if(!(w = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
nuclear@0 297 vis->visual, xattr_mask, &xattr))) {
nuclear@0 298 printf("failed to create window\n");
nuclear@0 299 glXDestroyContext(dpy, ctx);
nuclear@0 300 XFree(vis);
nuclear@0 301 return 0;
nuclear@0 302 }
nuclear@0 303 XFree(vis);
nuclear@0 304
nuclear@0 305 evmask = StructureNotifyMask | VisibilityChangeMask | KeyPressMask |
nuclear@0 306 ExposureMask;
nuclear@0 307 XSelectInput(dpy, w, evmask);
nuclear@0 308
nuclear@0 309 XSetWMProtocols(dpy, w, &xa_wm_del_win, 1);
nuclear@0 310
nuclear@0 311 chint.res_name = chint.res_class = "xgetshape";
nuclear@0 312 XSetClassHint(dpy, w, &chint);
nuclear@0 313
nuclear@0 314 set_window_title(w, "GL xgetshape");
nuclear@0 315
nuclear@0 316 glXMakeCurrent(dpy, w, ctx);
nuclear@0 317 XMapWindow(dpy, w);
nuclear@0 318 return w;
nuclear@0 319 }
nuclear@0 320
nuclear@0 321 void set_window_title(Window win, const char *title)
nuclear@0 322 {
nuclear@0 323 XTextProperty wm_name;
nuclear@0 324 XStringListToTextProperty((char**)&title, 1, &wm_name);
nuclear@0 325 XSetWMName(dpy, win, &wm_name);
nuclear@0 326 XSetWMIconName(dpy, win, &wm_name);
nuclear@0 327 XFree(wm_name.value);
nuclear@0 328 }
nuclear@0 329
nuclear@0 330 int get_window_shape(Window win, struct image *img)
nuclear@0 331 {
nuclear@0 332 Bool buse, cuse;
nuclear@0 333 int bx, by, cx, cy;
nuclear@0 334 unsigned int bw, bh, cw, ch;
nuclear@0 335 int kind;
nuclear@2 336 int xoffs, yoffs, w, h;
nuclear@0 337 XRectangle *rects;
nuclear@0 338 int i, rect_count, rect_order;
nuclear@0 339
nuclear@0 340 XShapeQueryExtents(dpy, win, &buse, &bx, &by, &bw, &bh,
nuclear@1 341 &cuse, &cx, &cy, &cw, &ch);
nuclear@2 342 if(buse) {
nuclear@2 343 w = bw;
nuclear@2 344 h = bh;
nuclear@2 345 xoffs = bx;
nuclear@2 346 yoffs = by;
nuclear@2 347 kind = ShapeBounding;
nuclear@2 348 } else if(cuse) {
nuclear@0 349 w = cw;
nuclear@0 350 h = ch;
nuclear@2 351 xoffs = cx;
nuclear@2 352 yoffs = cy;
nuclear@0 353 kind = ShapeClip;
nuclear@0 354 } else {
nuclear@0 355 fprintf(stderr, "XShapeQueryExtents returned no extents\n");
nuclear@0 356 return -1;
nuclear@0 357 }
nuclear@0 358
nuclear@0 359 if(image_create(img, w, h) == -1) {
nuclear@0 360 fprintf(stderr, "failed to create shape image (%dx%d)\n", w, h);
nuclear@0 361 return -1;
nuclear@0 362 }
nuclear@0 363 image_clear(img, 0, 0, 0, 0);
nuclear@0 364
nuclear@0 365 if(!(rects = XShapeGetRectangles(dpy, win, kind, &rect_count, &rect_order))) {
nuclear@0 366 fprintf(stderr, "failed to get the shape rectangles\n");
nuclear@0 367 image_destroy(img);
nuclear@0 368 return -1;
nuclear@0 369 }
nuclear@0 370
nuclear@0 371 for(i=0; i<rect_count; i++) {
nuclear@2 372 image_fillrect(img, rects[i].x - xoffs, rects[i].y - yoffs, rects[i].width, rects[i].height,
nuclear@0 373 255, 255, 255, 255);
nuclear@0 374 }
nuclear@0 375 return 0;
nuclear@0 376 }
nuclear@1 377
nuclear@1 378 XID get_window_id(Display *dpy, int screen, int button)
nuclear@1 379 {
nuclear@1 380 Window root; /* the current root */
nuclear@1 381 Window retwin = None; /* the window that got selected */
nuclear@1 382 int retbutton = -1; /* button used to select window */
nuclear@1 383 int pressed = 0; /* count of number of buttons pressed */
nuclear@1 384
nuclear@1 385 #define MASK (ButtonPressMask | ButtonReleaseMask)
nuclear@1 386
nuclear@1 387 root = RootWindow(dpy, screen);
nuclear@1 388 XSync(dpy, 0); /* give xterm a chance */
nuclear@1 389
nuclear@1 390 if(XGrabPointer(dpy, root, False, MASK, GrabModeSync, GrabModeAsync,
nuclear@1 391 None, sel_cursor, CurrentTime) != GrabSuccess) {
nuclear@1 392 fprintf (stderr, "unable to grab cursor\n");
nuclear@1 393 return 0;
nuclear@1 394 }
nuclear@1 395
nuclear@1 396 while (retwin == None || pressed != 0) {
nuclear@1 397 XEvent event;
nuclear@1 398
nuclear@1 399 XAllowEvents (dpy, SyncPointer, CurrentTime);
nuclear@1 400 XWindowEvent (dpy, root, MASK, &event);
nuclear@1 401
nuclear@1 402 switch (event.type) {
nuclear@1 403 case ButtonPress:
nuclear@1 404 if (retwin == None) {
nuclear@1 405 retbutton = event.xbutton.button;
nuclear@1 406 retwin = ((event.xbutton.subwindow != None) ?
nuclear@1 407 event.xbutton.subwindow : root);
nuclear@1 408 }
nuclear@1 409 pressed++;
nuclear@1 410 continue;
nuclear@1 411
nuclear@1 412 case ButtonRelease:
nuclear@1 413 if (pressed > 0) pressed--;
nuclear@1 414 continue;
nuclear@1 415 } /* end switch */
nuclear@1 416 } /* end for */
nuclear@1 417
nuclear@1 418 XUngrabPointer (dpy, CurrentTime);
nuclear@1 419 XSync (dpy, 0);
nuclear@1 420
nuclear@1 421 return ((button == -1 || retbutton == button) ? retwin : None);
nuclear@1 422 }