]> mj.ucw.cz Git - osdd.git/blob - test.c
ced576fe564ce03b98d6e32ccad76ad5d61d0b68
[osdd.git] / test.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <poll.h>
6 #include <getopt.h>
7 #include <X11/Xlib.h>
8 #include <X11/Xatom.h>
9 #include <X11/extensions/shape.h>
10 #include <X11/extensions/render.h>
11 #include <X11/Xft/Xft.h>
12
13 #define DEBUG
14 #include "osd.h"
15
16 struct osd_state {
17   Display *dpy;
18   int screen;
19   Visual *visual;
20   Colormap cmap;
21   int depth;
22   int screen_width;
23   int screen_height;
24   Window win;
25   Pixmap mask_bitmap;
26   Pixmap image_pixmap;
27   GC gc;
28   GC mask_gc;
29
30   // Xft stuff
31   XftFont *font;
32   XftDraw *mask_draw;
33   XftDraw *image_draw;
34 };
35
36 static void
37 stay_on_top(struct osd_state *osd)
38 {
39   /* Heavily inspired by a similar function in libxosd */
40   Window root = DefaultRootWindow(osd->dpy);
41   int format;
42   unsigned long nitems, bytes_after;
43   unsigned char *prop = NULL;
44   Atom type;
45
46   // Gnome-compliant way
47   Atom gnome = XInternAtom(osd->dpy, "_WIN_SUPPORTING_WM_CHECK", False);
48   if (XGetWindowProperty(osd->dpy, root, gnome, 0, 16384, False, AnyPropertyType,
49                          &type, &format, &nitems, &bytes_after, &prop) == Success &&
50       nitems > 0)
51     {
52       DBG("stay_on_top: Gnome mode\n");
53       // FIXME: check capabilities
54       XClientMessageEvent e;
55       memset(&e, 0, sizeof(e));
56       e.type = ClientMessage;
57       e.window = osd->win;
58       e.message_type = XInternAtom(osd->dpy, "_WIN_LAYER", False);
59       e.format = 32;
60       e.data.l[0] = 6;  // WIN_LAYER_ONTOP */
61       XSendEvent(osd->dpy, root, False, SubstructureNotifyMask, (XEvent *) &e);
62       XFree(prop);
63       return;
64     }
65
66   // NetWM-compliant way
67   Atom net_wm = XInternAtom(osd->dpy, "_NET_SUPPORTED", False);
68   if (XGetWindowProperty(osd->dpy, root, net_wm, 0, 16384, False, AnyPropertyType,
69                               &type, &format, &nitems, &bytes_after, &prop) == Success &&
70            nitems > 0)
71     {
72       DBG("stay_on_top: NetWM mode\n");
73       XEvent e;
74       memset(&e, 0, sizeof(e));
75       e.xclient.type = ClientMessage;
76       e.xclient.message_type = XInternAtom(osd->dpy, "_NET_WM_STATE", False);
77       e.xclient.display = osd->dpy;
78       e.xclient.window = osd->win;
79       e.xclient.format = 32;
80       e.xclient.data.l[0] = 1;  // _NET_WM_STATE_ADD
81       e.xclient.data.l[1] = XInternAtom(osd->dpy, "_NET_WM_STATE_STAYS_ON_TOP", False);
82       XSendEvent(osd->dpy, root, False, SubstructureRedirectMask, &e);
83       XFree(prop);
84       return;
85     }
86
87   DBG("stay_on_top: WM does not support any known protocol\n");
88 }
89
90 int
91 main(int argc, char **argv)
92 {
93   XInitThreads();
94
95   struct osd_state osd_static;
96   bzero(&osd_static, sizeof(osd_static));
97   struct osd_state *osd = &osd_static;
98
99   osd->dpy = XOpenDisplay(NULL);
100   if (!osd->dpy)
101     die("Cannot open display");
102
103   int event_basep, error_basep;
104   if (!XShapeQueryExtension(osd->dpy, &event_basep, &error_basep))
105     die("XShape extension not supported by X server, giving up");
106
107   osd->screen = XDefaultScreen(osd->dpy);
108   osd->visual = XDefaultVisual(osd->dpy, osd->screen);
109   osd->depth = XDefaultDepth(osd->dpy, osd->screen);
110   osd->cmap = DefaultColormap(osd->dpy, osd->screen);
111
112   // These can change. And what about Xinerama?
113   osd->screen_width = XDisplayWidth(osd->dpy, osd->screen);
114   osd->screen_height = XDisplayHeight(osd->dpy, osd->screen);
115
116   XSetWindowAttributes win_attr = {
117     .override_redirect = 1,
118   };
119   osd->win = XCreateWindow(osd->dpy,
120         XRootWindow(osd->dpy, osd->screen),
121         0, 0,
122         osd->screen_width, osd->screen_height,
123         0,
124         osd->depth,
125         CopyFromParent,
126         osd->visual,
127         CWOverrideRedirect,
128         &win_attr);
129   XStoreName(osd->dpy, osd->win, "OSD");
130   stay_on_top(osd);
131
132   // FIXME: Size
133   osd->mask_bitmap = XCreatePixmap(osd->dpy, osd->win, osd->screen_width, osd->screen_height, 1);
134   osd->image_pixmap = XCreatePixmap(osd->dpy, osd->win, osd->screen_width, osd->screen_height, osd->depth);
135   DBG("depth = %d\n", osd->depth);
136
137   XGCValues gcv = {
138     .graphics_exposures = 0,
139   };
140   osd->gc = XCreateGC(osd->dpy, osd->win, GCGraphicsExposures, &gcv);
141   osd->mask_gc = XCreateGC(osd->dpy, osd->mask_bitmap, GCGraphicsExposures, &gcv);
142
143   XSetBackground(osd->dpy, osd->gc, BlackPixel(osd->dpy, osd->screen));
144   XColor shadow_color = { .red = 0xffff, .green = 0xffff, .blue = 0 };
145   XAllocColor(osd->dpy, osd->cmap, &shadow_color);
146   XSetForeground(osd->dpy, osd->gc, shadow_color.pixel);
147
148   XSetBackground(osd->dpy, osd->mask_gc, WhitePixel(osd->dpy, osd->screen));
149   XSetForeground(osd->dpy, osd->mask_gc, BlackPixel(osd->dpy, osd->screen));
150
151   XFillRectangle(osd->dpy, osd->image_pixmap, osd->gc, 0, 0, osd->screen_width, osd->screen_height);
152
153   XFillRectangle(osd->dpy, osd->mask_bitmap, osd->mask_gc, 0, 0, osd->screen_width, osd->screen_height);
154
155   osd->font = XftFontOpenName(osd->dpy, osd->screen, "times-64");
156   if (!osd->font)
157     die("Cannot open font");
158   DBG("Font: asc=%d desc=%d ht=%d\n", osd->font->ascent, osd->font->descent, osd->font->height);
159
160   osd->mask_draw = XftDrawCreateBitmap(osd->dpy, osd->mask_bitmap);
161   if (!osd->mask_draw)
162     die("Cannot create XftDraw");
163
164   osd->image_draw = XftDrawCreate(osd->dpy, osd->image_pixmap, osd->visual, osd->cmap);
165   if (!osd->image_draw)
166     die("Cannot create XftDraw");
167
168   XRenderColor xrc = { .red = 0, .green = 0xffff, .blue = 0, .alpha = 0xffff };
169   XftColor xfc;
170   if (!XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &xrc, &xfc))
171     die("XftColorAllocValue failed");
172   const unsigned char str[] = "Žluťoučká vlkodlačice";
173   XftDrawStringUtf8(osd->image_draw, &xfc, osd->font, 100, 100, str, strlen((char *) str));
174
175   XRenderColor xrcm = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff };
176   XftColor xfcm;
177   if (!XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &xrcm, &xfcm))
178     die("XftColorAllocValue failed");
179
180   // This is slow, but unlike the method used by libxosd, the result isn't ugly.
181   int outline = 3;
182   for (int dx = -outline; dx <= outline; dx++)
183     for (int dy = -outline; dy <= outline; dy++)
184       if (dx*dx + dy*dy <= outline*outline)
185         XftDrawStringUtf8(osd->mask_draw, &xfcm, osd->font, 100 + dx, 100 + dy, str, strlen((char *) str));
186
187 #if 0
188   XGlyphInfo gi;
189   XftTextExtentsUtf8(osd->dpy, osd->font, str, strlen((char *) str), &gi);
190   DBG("Glyph info: (%d,%d)+(%d,%d) off (%d,%d)\n", gi.x, gi.y, gi.width, gi.height, gi.xOff, gi.yOff);
191   XftDrawRect(osd->image_draw, &xfc, 100 + gi.x, 100 - gi.y, gi.width, gi.height);
192 #endif
193
194   XShapeCombineMask(osd->dpy, osd->win, ShapeBounding, 0, 0, osd->mask_bitmap, ShapeSet);
195
196   XSelectInput(osd->dpy, osd->win, ExposureMask);
197   XMapRaised(osd->dpy, osd->win);
198   XFlush(osd->dpy);
199
200   struct pollfd pfd = {
201     .fd = ConnectionNumber(osd->dpy),
202     .events = POLLIN,
203   };
204
205   for (;;)
206     {
207       timestamp_t now = get_current_time();
208       timestamp_t wait_until = now - 1;
209
210       DBG("... waiting for %d ms\n", (int)(wait_until - now));
211       poll(&pfd, 1, wait_until - now);
212       if (pfd.revents & POLLIN)
213         {
214           while (XPending(osd->dpy))
215             {
216               XEvent ev;
217               XNextEvent(osd->dpy, &ev);
218               switch (ev.type)
219                 {
220                 case Expose:
221                   {
222                     XExposeEvent *ex = &ev.xexpose;
223                     DBG("Expose cnt=%d (%d,%d)+(%d,%d)\n", ex->count, ex->x, ex->y, ex->width, ex->height);
224                     XCopyArea(osd->dpy, osd->image_pixmap, osd->win, osd->gc, ex->x, ex->y, ex->width, ex->height, ex->x, ex->y);
225                     break;
226                   }
227                 default:
228                   DBG("Event %d\n", ev.type);
229                 }
230             }
231         }
232     }
233
234   // FIXME: Cleanup
235 }