xgetshape

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