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@1
|
336 int 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@0
|
342 if(cuse) {
|
nuclear@0
|
343 w = cw;
|
nuclear@0
|
344 h = ch;
|
nuclear@0
|
345 kind = ShapeClip;
|
nuclear@0
|
346 } else if(buse) {
|
nuclear@0
|
347 w = bw;
|
nuclear@0
|
348 h = bh;
|
nuclear@0
|
349 kind = ShapeBounding;
|
nuclear@0
|
350 } else {
|
nuclear@0
|
351 fprintf(stderr, "XShapeQueryExtents returned no extents\n");
|
nuclear@0
|
352 return -1;
|
nuclear@0
|
353 }
|
nuclear@0
|
354
|
nuclear@0
|
355 if(image_create(img, w, h) == -1) {
|
nuclear@0
|
356 fprintf(stderr, "failed to create shape image (%dx%d)\n", w, h);
|
nuclear@0
|
357 return -1;
|
nuclear@0
|
358 }
|
nuclear@0
|
359 image_clear(img, 0, 0, 0, 0);
|
nuclear@0
|
360
|
nuclear@0
|
361 if(!(rects = XShapeGetRectangles(dpy, win, kind, &rect_count, &rect_order))) {
|
nuclear@0
|
362 fprintf(stderr, "failed to get the shape rectangles\n");
|
nuclear@0
|
363 image_destroy(img);
|
nuclear@0
|
364 return -1;
|
nuclear@0
|
365 }
|
nuclear@0
|
366
|
nuclear@0
|
367 for(i=0; i<rect_count; i++) {
|
nuclear@0
|
368 image_fillrect(img, rects[i].x, rects[i].y, rects[i].width, rects[i].height,
|
nuclear@0
|
369 255, 255, 255, 255);
|
nuclear@0
|
370 }
|
nuclear@0
|
371 return 0;
|
nuclear@0
|
372 }
|
nuclear@1
|
373
|
nuclear@1
|
374 XID get_window_id(Display *dpy, int screen, int button)
|
nuclear@1
|
375 {
|
nuclear@1
|
376 Window root; /* the current root */
|
nuclear@1
|
377 Window retwin = None; /* the window that got selected */
|
nuclear@1
|
378 int retbutton = -1; /* button used to select window */
|
nuclear@1
|
379 int pressed = 0; /* count of number of buttons pressed */
|
nuclear@1
|
380
|
nuclear@1
|
381 #define MASK (ButtonPressMask | ButtonReleaseMask)
|
nuclear@1
|
382
|
nuclear@1
|
383 root = RootWindow(dpy, screen);
|
nuclear@1
|
384 XSync(dpy, 0); /* give xterm a chance */
|
nuclear@1
|
385
|
nuclear@1
|
386 if(XGrabPointer(dpy, root, False, MASK, GrabModeSync, GrabModeAsync,
|
nuclear@1
|
387 None, sel_cursor, CurrentTime) != GrabSuccess) {
|
nuclear@1
|
388 fprintf (stderr, "unable to grab cursor\n");
|
nuclear@1
|
389 return 0;
|
nuclear@1
|
390 }
|
nuclear@1
|
391
|
nuclear@1
|
392 while (retwin == None || pressed != 0) {
|
nuclear@1
|
393 XEvent event;
|
nuclear@1
|
394
|
nuclear@1
|
395 XAllowEvents (dpy, SyncPointer, CurrentTime);
|
nuclear@1
|
396 XWindowEvent (dpy, root, MASK, &event);
|
nuclear@1
|
397
|
nuclear@1
|
398 switch (event.type) {
|
nuclear@1
|
399 case ButtonPress:
|
nuclear@1
|
400 if (retwin == None) {
|
nuclear@1
|
401 retbutton = event.xbutton.button;
|
nuclear@1
|
402 retwin = ((event.xbutton.subwindow != None) ?
|
nuclear@1
|
403 event.xbutton.subwindow : root);
|
nuclear@1
|
404 }
|
nuclear@1
|
405 pressed++;
|
nuclear@1
|
406 continue;
|
nuclear@1
|
407
|
nuclear@1
|
408 case ButtonRelease:
|
nuclear@1
|
409 if (pressed > 0) pressed--;
|
nuclear@1
|
410 continue;
|
nuclear@1
|
411 } /* end switch */
|
nuclear@1
|
412 } /* end for */
|
nuclear@1
|
413
|
nuclear@1
|
414 XUngrabPointer (dpy, CurrentTime);
|
nuclear@1
|
415 XSync (dpy, 0);
|
nuclear@1
|
416
|
nuclear@1
|
417 return ((button == -1 || retbutton == button) ? retwin : None);
|
nuclear@1
|
418 }
|