# HG changeset patch # User John Tsiombikas # Date 1446504128 -7200 # Node ID 2f02f100b20f3d2bacbc2f386b0af6c7e451b7a7 getting the window shape of another window diff -r 000000000000 -r 2f02f100b20f .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Tue Nov 03 00:42:08 2015 +0200 @@ -0,0 +1,5 @@ +\.o$ +\.swp$ +\.d$ +\.ppm$ +^xgetshape$ diff -r 000000000000 -r 2f02f100b20f Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Tue Nov 03 00:42:08 2015 +0200 @@ -0,0 +1,13 @@ +src = $(wildcard src/*.c) +obj = $(src:.c=.o) +bin = xgetshape + +CFLAGS = -pedantic -Wall -g +LDFLAGS = -lGL -lX11 -lXext + +$(bin): $(obj) + $(CC) -o $@ $(obj) $(LDFLAGS) + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff -r 000000000000 -r 2f02f100b20f src/image.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image.c Tue Nov 03 00:42:08 2015 +0200 @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include "image.h" + +int image_create(struct image *img, int w, int h) +{ + img->width = w; + img->height = h; + if(!(img->pixels = malloc(w * h * 4))) { + return -1; + } + return 0; +} + +void image_destroy(struct image *img) +{ + free(img->pixels); + img->pixels = 0; +} + +int image_save(struct image *img, const char *rgbname, const char *aname) +{ + int i; + FILE *fp; + unsigned char *pptr; + + if(rgbname) { + if(!(fp = fopen(rgbname, "wb"))) { + fprintf(stderr, "failed to open %s for writing: %s\n", rgbname, strerror(errno)); + return -1; + } + + fprintf(fp, "P6\n%d %d\n255\n", img->width, img->height); + pptr = img->pixels; + for(i=0; iwidth * img->height; i++) { + fputc(*pptr++, fp); + fputc(*pptr++, fp); + fputc(*pptr++, fp); + ++pptr; + } + fclose(fp); + } + if(aname) { + if(!(fp = fopen(aname, "wb"))) { + fprintf(stderr, "failed to open %s for writing: %s\n", rgbname, strerror(errno)); + return -1; + } + + fprintf(fp, "P6\n%d %d\n255\n", img->width, img->height); + pptr = img->pixels; + for(i=0; iwidth * img->height; i++) { + int c = pptr[3]; + pptr += 4; + fputc(c, fp); + fputc(c, fp); + fputc(c, fp); + } + fclose(fp); + } + return 0; +} + +int image_clear(struct image *img, int r, int g, int b, int a) +{ + return image_fillrect(img, 0, 0, img->width, img->height, r, g, b, a); +} + +int image_fillrect(struct image *img, int x, int y, int w, int h, int r, int g, int b, int a) +{ + int i, j; + unsigned char *pptr; + + if(x < 0) { + w += x; + x = 0; + } + if(y < 0) { + h += y; + y = 0; + } + if(x + w >= img->width) { + w = img->width - x; + } + if(y + h >= img->height) { + h = img->height - y; + } + if(w <= 0 || h <= 0) { + return -1; + } + + pptr = img->pixels + (y * img->width + x) * 4; + for(i=0; iwidth - w) * 4; + } + return 0; +} + +int image_chess(struct image *img, int sz, int r0, int g0, int b0, int r1, int g1, int b1) +{ + int i, j; + unsigned char *pptr = img->pixels; + + for(i=0; iheight; i++) { + for(j=0; jwidth; j++) { + int chess = ((i / sz) & 1) == ((j / sz) & 1); + *pptr++ = chess ? r0 : r1; + *pptr++ = chess ? g0 : g1; + *pptr++ = chess ? b0 : b1; + *pptr++ = 255; + } + } + return 0; +} diff -r 000000000000 -r 2f02f100b20f src/image.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image.h Tue Nov 03 00:42:08 2015 +0200 @@ -0,0 +1,18 @@ +#ifndef IMAGE_H_ +#define IMAGE_H_ + +struct image { + int width, height; + unsigned char *pixels; +}; + +int image_create(struct image *img, int w, int h); +void image_destroy(struct image *img); + +int image_save(struct image *img, const char *rgbname, const char *aname); + +int image_clear(struct image *img, int r, int g, int b, int a); +int image_fillrect(struct image *img, int x, int y, int w, int h, int r, int g, int b, int a); +int image_chess(struct image *img, int sz, int r0, int g0, int b0, int r1, int g1, int b1); + +#endif /* IMAGE_H_ */ diff -r 000000000000 -r 2f02f100b20f src/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.c Tue Nov 03 00:42:08 2015 +0200 @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include +#include "image.h" +#include "texture.h" + +void redraw(void); +void reshape(int x, int y); +int handle_event(XEvent *ev); + +/* X helper functions */ +Window create_window(int xsz, int ysz); +void set_window_title(Window win, const char *title); +int get_window_shape(Window win, struct image *img); + +Display *dpy; +Window win; +GLXContext ctx; +int width, height; +int mapped; +int redisp_pending; +Atom xa_wm_prot, xa_wm_del_win; +struct image img; +struct texture tex; +struct texture chess_tex; +float aspect; + +unsigned int target_xid = 0; + +int main(int argc, char **argv) +{ + int event_base, error_base; + char *endp; + struct image chess_img; + + if(!argv[1]) { + fprintf(stderr, "pass the window id to use\n"); + return 1; + } + if(!(target_xid = strtol(argv[1], &endp, 0))) { + fprintf(stderr, "invalid argument: %s\n", argv[1]); + return 1; + } + + if(!(dpy = XOpenDisplay(0))) { + fprintf(stderr, "failed to open display\n"); + return 1; + } + xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False); + xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + + if(!XShapeQueryExtension(dpy, &event_base, &error_base)) { + fprintf(stderr, "shape extension unsupported by this X server\n"); + XCloseDisplay(dpy); + return 1; + } + if(get_window_shape(target_xid, &img) == -1) { + XCloseDisplay(dpy); + return 1; + } + + if(!(win = create_window(1280, 800))) { + return 1; + } + + image_texture(&tex, &img); + + image_create(&chess_img, 256, 256); + image_chess(&chess_img, 8, 255, 128, 64, 64, 128, 255); + image_texture(&chess_tex, &chess_img); + image_destroy(&chess_img); + + for(;;) { + XEvent ev; + XNextEvent(dpy, &ev); + + if(handle_event(&ev) == -1) { + break; + } + } + + XDestroyWindow(dpy, win); + XCloseDisplay(dpy); + return 0; +} + +void redraw(void) +{ + glClearColor(0.4, 0.4, 0.4, 1); + glClear(GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, chess_tex.id); + + + /* background */ + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(-aspect, -1); + glTexCoord2f(1, 0); glVertex2f(aspect, -1); + glTexCoord2f(1, 1); glVertex2f(aspect, 1); + glTexCoord2f(0, 1); glVertex2f(-aspect, 1); + glEnd(); + + /* draw the window shape */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBindTexture(GL_TEXTURE_2D, tex.id); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef((float)tex.width / (float)tex.tex_width, + (float)tex.height / (float)tex.tex_height, + 1.0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glScalef(0.7, 0.7, 0.7); + + /* shadow */ + glPushMatrix(); + glTranslatef(0.1, -0.1, 0); + glBegin(GL_QUADS); + glColor3f(0, 0, 0); + glTexCoord2f(0, 1); glVertex2f(-1, -1); + glTexCoord2f(1, 1); glVertex2f(1, -1); + glTexCoord2f(1, 0); glVertex2f(1, 1); + glTexCoord2f(0, 0); glVertex2f(-1, 1); + glEnd(); + glPopMatrix(); + + /* window */ + glBegin(GL_QUADS); + glColor3f(1, 1, 0); + glTexCoord2f(0, 1); glVertex2f(-1, -1); + glTexCoord2f(1, 1); glVertex2f(1, -1); + glTexCoord2f(1, 0); glVertex2f(1, 1); + glTexCoord2f(0, 0); glVertex2f(-1, 1); + glEnd(); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glDisable(GL_BLEND); + + glXSwapBuffers(dpy, win); +} + +void reshape(int x, int y) +{ + aspect = (float)x / (float)y; + glViewport(0, 0, x, y); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glScalef(1.0 / aspect, 1.0, 1.0); +} + +int handle_event(XEvent *ev) +{ + switch(ev->type) { + case MapNotify: + case UnmapNotify: + mapped = ev->type == MapNotify ? 1 : 0; + break; + + case Expose: + if(mapped && ev->xexpose.count == 0) { + redraw(); + } + break; + + case KeyPress: + { + KeySym sym = XLookupKeysym(&ev->xkey, 0); + + switch(sym) { + case XK_Escape: + return -1; + } + } + break; + + case ConfigureNotify: + if(ev->xconfigure.width != width || ev->xconfigure.height != height) { + width = ev->xconfigure.width; + height = ev->xconfigure.height; + reshape(width, height); + } + break; + + case ClientMessage: + if(ev->xclient.message_type == xa_wm_prot) { + if(ev->xclient.data.l[0] == xa_wm_del_win) { + return -1; + } + } + break; + } + return 0; +} + +Window create_window(int xsz, int ysz) +{ + Window w, root; + XVisualInfo *vis; + XClassHint chint; + XSetWindowAttributes xattr; + unsigned int evmask, xattr_mask; + int scr; + int glxattr[] = { + GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_USE_GL, 1, + None + }; + + scr = DefaultScreen(dpy); + root = RootWindow(dpy, scr); + + if(!(vis = glXChooseVisual(dpy, scr, glxattr))) { + printf("failed to find a suitable visual\n"); + return 0; + } + if(!(ctx = glXCreateContext(dpy, vis, 0, True))) { + XFree(vis); + return 0; + } + + xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr); + xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone); + xattr_mask = CWColormap | CWBackPixel | CWBorderPixel; + + if(!(w = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput, + vis->visual, xattr_mask, &xattr))) { + printf("failed to create window\n"); + glXDestroyContext(dpy, ctx); + XFree(vis); + return 0; + } + XFree(vis); + + evmask = StructureNotifyMask | VisibilityChangeMask | KeyPressMask | + ExposureMask; + XSelectInput(dpy, w, evmask); + + XSetWMProtocols(dpy, w, &xa_wm_del_win, 1); + + chint.res_name = chint.res_class = "xgetshape"; + XSetClassHint(dpy, w, &chint); + + set_window_title(w, "GL xgetshape"); + + glXMakeCurrent(dpy, w, ctx); + XMapWindow(dpy, w); + return w; +} + +void set_window_title(Window win, const char *title) +{ + XTextProperty wm_name; + XStringListToTextProperty((char**)&title, 1, &wm_name); + XSetWMName(dpy, win, &wm_name); + XSetWMIconName(dpy, win, &wm_name); + XFree(wm_name.value); +} + +int get_window_shape(Window win, struct image *img) +{ + Bool buse, cuse; + int bx, by, cx, cy; + unsigned int bw, bh, cw, ch; + int kind; + int x, y, w, h; + XRectangle *rects; + int i, rect_count, rect_order; + + XShapeQueryExtents(dpy, win, &buse, &bx, &by, &bw, &bh, + &cuse, &cx, &cy, &bw, &bh); + if(cuse) { + x = cx; + y = cy; + w = cw; + h = ch; + kind = ShapeClip; + } else if(buse) { + x = bx; + y = by; + w = bw; + h = bh; + kind = ShapeBounding; + } else { + fprintf(stderr, "XShapeQueryExtents returned no extents\n"); + return -1; + } + + if(image_create(img, w, h) == -1) { + fprintf(stderr, "failed to create shape image (%dx%d)\n", w, h); + return -1; + } + image_clear(img, 0, 0, 0, 0); + + if(!(rects = XShapeGetRectangles(dpy, win, kind, &rect_count, &rect_order))) { + fprintf(stderr, "failed to get the shape rectangles\n"); + image_destroy(img); + return -1; + } + + for(i=0; i +#include "texture.h" + +static int next_pow2(int x); + +int image_texture(struct texture *tex, struct image *img) +{ + glGenTextures(1, &tex->id); + glBindTexture(GL_TEXTURE_2D, tex->id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + tex->width = img->width; + tex->height = img->height; + tex->tex_width = next_pow2(tex->width); + tex->tex_height = next_pow2(tex->height); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->tex_width, tex->tex_height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex->width, tex->height, + GL_RGBA, GL_UNSIGNED_BYTE, img->pixels); + return 0; +} + +static int next_pow2(int x) +{ + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + return x + 1; +} diff -r 000000000000 -r 2f02f100b20f src/texture.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texture.h Tue Nov 03 00:42:08 2015 +0200 @@ -0,0 +1,13 @@ +#ifndef TEXTURE_H_ +#define TEXTURE_H_ + +#include "image.h" + +struct texture { + unsigned int id; + int width, height, tex_width, tex_height; +}; + +int image_texture(struct texture *tex, struct image *img); + +#endif /* TEXTURE_H_ */