1 #include <X11/Xlib.h>
2 #include <X11/Xutil.h>
3 #include <X11/Xatom.h>
4 #include <X11/cursorfont.h>
5 #include <math.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <time.h>
9 #include <sys/time.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #define clamp(min, x, max) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x))
14 #define sign(x) ((x) > 0 ? 1 : (x) < 0 ? -1 : 0)
15 #define abs(x) ((x) >= 0 ? (x) : -(x))
16 #define min(a, b) ((a) <= (b) ? (a) : (b))
17 #define max(a, b) ((a) >= (b) ? (a) : (b))
18
19 #ifdef NEED_HYPOT
20 /* SVID 3, BSD 4.3, XOpen, C99 and GNU all have hypot(). Why don't you? */
21 #define hypot(a, b) sqrt((a)*(a) + (b)*(b))
22 #endif
23
24 /* Look and feel parameters to play with: */
25 int length = 300;
26 int thumb = 100;
27 int thickness = 20;
28 int padding = 5;
29 int depth = 2;
30 double relief_frac = .1; /*relief area / thickness, 0 => relief doesn't scale*/
31 XColor trough = {0, 0xa3a3, 0xa3a3, 0xb3b3, 0, 0};
32 XColor bg = {0, 0xc6c6, 0xc6c6, 0xd6d6, 0, 0};
33 XColor fill = {0, 0xb6b6, 0x3030, 0x6060, 0, 0};
34 double shade = .5001; /* 0 => shadows black, hilights white; 1 => no shading */
35 /* for relief, 0 => raised, 1 => sunk, 2 => ridge, 3 => groove */
36 int prog_relief = 1; int sbar_relief = 1; int slider_relief = 0;
37 int arrow_relief = 0; int dimple_relief = 1;
38 int arrow_change = 1; /* these bits will flip when pressed */
39 double dimple = .3001; /* size / scrollbar thickness, 0 for none */
40 double font_frac = .6001;/* text fills 60% of the height of the progresss bar*/
41 /* Note that the progress bar prefers scalable fonts, so that it can keep
42 the same proportions when the window is resized. Depending on how modern
43 your X installation is, this may be nontrivial.
44 * The best case is if you have a font that includes both hand-edited
45 bitmaps for small sizes and outlines that can be scaled arbitrarily.
46 All recent X releases come with bitmaps provided by Adobe for Helvetica,
47 so if you also have a corresponding Type 1 outline, that's the best
48 choice:
49 (bitmaps for sizes 8, 10, 11, 12, 14, 17, 18, 20, 24, 25, and 34) */
50 /*char *fontname="-adobe-helvetica-medium-r-normal--%d-*-*-*-*-*-iso8859-1";*/
51 /* Appending the following subsetting hint will speed up resizes, at the
52 expense of excluding premade bitmaps:
53 "[48 49 50 51 52 53 54 55 56 57 37]"; */
54 /* (If you're using Debian Linux like me, you'll need to install the
55 gsfonts and gsfonts-x11 packages to get the Type 1 versions. The
56 outline isn't the genuine Adobe version; it's a free clone that
57 can also be accessed directly (without Adobe's bitmaps) as)*/
58 char *fontname = "-urw-nimbus sans l-regular-r-normal--%d-*-*-*-*-*-iso8859-1";
59 /* * Recent X releases also include some scalable fonts, though not any
60 sans-serif ones. In the following, adobe-utopia can be replaced by
61 adobe-courier, bitstream-courier, or bitstream-charter:
62 char *fontname = "-adobe-utopia-medium-r-normal--%d-*-*-*-*-*-iso8859-1";
63 * Also, recent X servers can scale bitmaps, though the results are usually
64 fairly ugly.
65 * If your X system predates XLFD (the 14-hyphen names), your font
66 selection is probably pretty miniscule; try to pick something around
67 12 pixels:
68 char *fontname = "7x13"; */
69 int cursor_id = XC_top_left_arrow;
70 int initial_delay = 150000; /* usecs */
71 int delay = 50000; /* usecs */
72 double accel = 0.5;
73 Bool smooth_progress = False; /* and un-smooth scrollbar */
74 int text_shading_style = 1; /* 0 => diagonalish, 1 => squarish */
75
76 /*
77 +--------------------------------------------------+
78 | main_win ^v padding [bg] |
79 | +----------------------------------------------+ |
80 | |#prog_win########### ^ |<|
81 | |##########[fill]#### :thickness |>|
82 | |#################### : |:|
83 | |<-------- length -----------:---------------->|:|
84 | |#################### V [trough] |:|
85 | +----------------------------------------------+:|
86 | ^v padding :|
87 | +----------------------------------------------+:|
88 | | sbar_win +------------------------+ |:|
89 |<| |+----+ slider_win +----+| [trough] |:|
90 |>|<-slider->|| <| |<-lt_win | |> || |:|
91 |:| pos |+----+ rt_win->+----+| |:|
92 |:| +------------------------+ |:|
93 |:+----------:------------------------:----------+:|
94 |: : ^v padding : :|
95 +:-----------:------------------------:-----------:+
96 : : : :
97 : : : :
98 padding :<------- thumb -------->: padding
99 */
100
101 Window main_win, prog_win, sbar_win, slider_win, lt_win, rt_win;
102 GC trough_gc, bg_gc, fill_gc, hilite_gc, shadow_gc;
103 double frac = 0;
104
105 Display *dpy;
106 Colormap cmap;
107
108 XColor shadow, hilite;
109 Atom delete_atom;
110
111 int fontsize;
112 XFontStruct *font;
113 char buf[256];
114
115 int total_wd, base_wd, total_ht, base_ht;
116
117 int inner_thick, slider_pos, pos_min, pos_max;
118
119 int lt_state, rt_state;
120
121 int text_wd, text_x, text_baseline;
122
123 Pixmap prog_pixmap;
124
125 int font_height;
126
127 /* floor : ceil :: int : away */
away(double x)128 int away(double x) {
129 return sign(x) * (int)(abs(x) + 0.9999);
130 }
131
draw_slope_poly(Window win,int relief,int dep,GC fill,XPoint * p,int n)132 void draw_slope_poly(Window win, int relief, int dep, GC fill,
133 XPoint *p, int n) {
134 GC tl, br;
135 GC *gc;
136 XPoint *ip;
137 int j;
138
139 if (relief > 1) {
140 draw_slope_poly(win, relief ^ 3, dep, fill, p, n);
141 /* tail recurse( */ relief &= 1; dep /= 2; fill = 0;
142 }
143 if (relief) {
144 tl = shadow_gc; br = hilite_gc;
145 } else {
146 tl = hilite_gc; br = shadow_gc;
147 }
148 gc = (GC*)malloc(n * sizeof(GC));
149 ip = (XPoint*)malloc(n * sizeof(XPoint));
150 for (j = 0; j < n; j++) {
151 int j_t_1 = (j + 1) % n; int j_t_2 = (j + 2) % n;
152 double ix = (double)p[j_t_1].x - (double)p[j].x;
153 double iy = (double)p[j_t_1].y - (double)p[j].y;
154 double ox, oy, in, on, mx, my, mn;
155 ox = (double)p[j_t_2].x - (double)p[j_t_1].x;
156 oy = (double)p[j_t_2].y - (double)p[j_t_1].y;
157 gc[j] = ix > iy ? tl : ix < iy ? br : ix > 0 ? tl : br;
158 if (ix * oy > iy * ox) {
159 ix = -ix; iy = -iy;
160 } else {
161 ox = -ox; oy = -oy;
162 }
163 in = hypot(ix, iy); ix /= in; iy /= in;
164 on = hypot(ox, oy); ox /= on; oy /= on;
165 mx = (ix + ox) / 2; my = (iy + oy) / 2;
166 mn = max(abs(mx), abs(my)); mx /= mn; my /= mn;
167 ip[j_t_1].x = p[j_t_1].x + away((double)(dep - 1) * mx);
168 ip[j_t_1].y = p[j_t_1].y + away((double)(dep - 1) * my);
169 }
170
171 if (fill)
172 XFillPolygon(dpy, win, fill, ip, n, Nonconvex, CoordModeOrigin);
173
174 for (j = 0; j < n; j++) {
175 XPoint quad[4];
176 int j_t_1 = (j + 1) % n;
177 quad[0] = p[j]; quad[1] = ip[j];
178 quad[2] = ip[j_t_1]; quad[3] = p[j_t_1];
179 XFillPolygon(dpy, win, gc[j], quad, 4, Convex, CoordModeOrigin);
180 XDrawLine(dpy, win, gc[j], p[j].x, p[j].y, p[j_t_1].x, p[j_t_1].y);
181 XDrawLine(dpy, win, gc[j], ip[j].x, ip[j].y, ip[j_t_1].x, ip[j_t_1].y);
182 }
183
184 for (j = 0; j < n; j++) {
185 int j_t_1 = (j + 1) % n;
186 if (gc[j] != gc[j_t_1])
187 XDrawLine(dpy, win, bg_gc, p[j_t_1].x, p[j_t_1].y,
188 ip[j_t_1].x, ip[j_t_1].y);
189 }
190 free(gc);
191 free(ip);
192 }
193
draw_slope(Window win,int x,int y,int wd,int ht,int relief)194 void draw_slope(Window win, int x, int y, int wd, int ht, int relief) {
195 XPoint rect[4];
196 rect[0].x = x; rect[0].y = y;
197 rect[1].x = x + wd - 1; rect[1].y = y;
198 rect[2].x = x + wd - 1; rect[2].y = y + ht - 1;
199 rect[3].x = x; rect[3].y = y + ht - 1;
200 draw_slope_poly(win, relief, depth, 0, rect, 4);
201 }
202
paint_arrow(Window win,int x,int y,int s,int dir,int relief)203 void paint_arrow(Window win, int x, int y, int s, int dir, int relief) {
204 XPoint p[3];
205 int S[4];
206 S[0] = 0; S[1] = s / 2; S[2] = s; S[3] = s / 2;
207 p[0].x = x + S[(dir + 1) % 4]; p[0].y = y + S[dir];
208 if (!(dir & 1) == !(dir & 2)) {
209 p[1].x = x + s; p[1].y = y + s;
210 } else {
211 p[1].x = x; p[1].y = y;
212 }
213 if (dir & 2) {
214 p[2].x = x + s; p[2].y = y;
215 } else {
216 p[2].x = x; p[2].y = y + s;
217 }
218 if (dir & 1) {
219 XPoint temp;
220 temp = p[1];
221 p[1] = p[2];
222 p[2] = temp;
223 }
224 draw_slope_poly(win, relief, depth, bg_gc, p, 3);
225 }
226
paint_slope_circle(Window win,int x,int y,int s,int dep,int relief)227 void paint_slope_circle(Window win, int x, int y, int s, int dep, int relief) {
228 GC tl, br;
229 int inner_x = x + dep; int inner_y = y + dep; int inner_s = s - 2 * dep;
230 if (relief & 1) {
231 tl = shadow_gc; br = hilite_gc;
232 } else {
233 tl = hilite_gc; br = shadow_gc;
234 }
235 XFillArc(dpy, win, bg_gc, x, y, s, s, 0, 360 * 64);
236 XFillArc(dpy, win, tl, x, y, s, s, 35 * 64, 160 * 64);
237 XDrawArc(dpy, win, tl, x, y, s, s, 35 * 64, 160 * 64);
238 XDrawArc(dpy, win, tl, inner_x, inner_y, inner_s, inner_s, 35*64, 160*64);
239 XFillArc(dpy, win, br, x, y, s, s, 215 * 64, 160 * 64);
240 XDrawArc(dpy, win, br, x, y, s, s, 215 * 64, 160 * 64);
241 XDrawArc(dpy, win, br, inner_x, inner_y, inner_s, inner_s, 215*64, 160*64);
242 if (relief & 2) {
243 int mid_x = x + dep / 2; int mid_y = y + dep / 2; int mid_s = s - dep;
244 XFillArc(dpy, win, br, mid_x, mid_y, mid_s, mid_s, 35*64, 160*64);
245 XFillArc(dpy, win, tl, mid_x, mid_y, mid_s, mid_s, 215*64, 160*64);
246 }
247 XFillArc(dpy, win, bg_gc, inner_x, inner_y, inner_s, inner_s, 0, 360*64);
248 }
249
paint_shaded_text(Drawable dable,int x,int y,XTextItem * text,int n)250 void paint_shaded_text(Drawable dable, int x, int y, XTextItem *text, int n) {
251 GC br_gc = shadow_gc;
252 GC tl_gc = hilite_gc;
253
254 if (text_shading_style)
255 XDrawText(dpy, dable, br_gc, x + 1, y + 1, text, n);
256 XDrawText(dpy, dable, br_gc, x, y + 1, text, n);
257 XDrawText(dpy, dable, br_gc, x + 1, y, text, n);
258
259 if (text_shading_style)
260 XDrawText(dpy, dable, tl_gc, x - 1, y - 1, text, n);
261 XDrawText(dpy, dable, tl_gc, x, y - 1, text, n);
262 XDrawText(dpy, dable, tl_gc, x - 1, y, text, n);
263
264 XDrawText(dpy, dable, bg_gc, x, y, text, n);
265 }
266
prog_update(double newfrac,int increm)267 void prog_update(double newfrac, int increm) {
268 double oldfrac = frac;
269 char str[5]; /* 1 0 0 % \0 */
270 int realend, end, n, wd;
271 XTextItem text[4];
272 frac = newfrac;
273 sprintf(str, "%d%%", (int)(frac * 100.0));
274 for (n = 0; str[n] != '\0'; n++) {
275 text[n].font = None;
276 text[n].nchars = 1;
277 text[n].chars = &str[n];
278 text[n].delta = 1;
279 }
280 if (str[0] == '1')
281 text[1].delta = -font_height / 10; /* kerning */
282 realend = (int)(frac * (double)(length - 2 * depth)) + depth;
283 if (increm) {
284 int newend = realend;
285 int oldend = (int)(oldfrac * (double)(length - 2 * depth)) + depth;
286 int x, *left, *right, count = 0;
287 if (newend > oldend) {
288 right = &newend; left = &oldend;
289 } else {
290 right = &oldend; left = &newend;
291 }
292 if (*left >= text_x && *left < text_x + text_wd) {
293 *left = text_x + text_wd - 1;
294 count++;
295 }
296 if (*right >= text_x && *right < text_x + text_wd) {
297 *right = text_x;
298 count++;
299 }
300 if (count == 2) {
301 /* do nothing */
302 } else if (newend > oldend) {
303 if (smooth_progress) {
304 for (x = oldend; x < newend; x++) {
305 XDrawLine(dpy, prog_win, fill_gc, x, depth, x,
306 thickness - depth - 1);
307 }
308 } else {
309 XFillRectangle(dpy, prog_win, fill_gc, oldend, depth,
310 newend - oldend, inner_thick);
311 }
312 } else if (newend < oldend) {
313 if (smooth_progress) {
314 for (x = oldend - 1; x >= newend; x--) {
315 XDrawLine(dpy, prog_win, trough_gc, x, depth,
316 x, thickness - depth - 1);
317 }
318 } else {
319 XFillRectangle(dpy, prog_win, trough_gc, newend, depth,
320 oldend - newend, inner_thick);
321 }
322 }
323 } else {
324 XFillRectangle(dpy, prog_win, fill_gc, depth, depth,
325 realend - depth, inner_thick);
326 }
327 end = clamp(0, realend - text_x, text_wd);
328 if (end > 0)
329 XFillRectangle(dpy, prog_pixmap, fill_gc, 0, 0, end, inner_thick);
330 if (end < text_wd)
331 XFillRectangle(dpy, prog_pixmap, trough_gc, end, 0,
332 text_wd - end, inner_thick);
333 wd = XTextWidth(font, str, n);
334 paint_shaded_text(prog_pixmap, 1 + (text_wd - wd) / 2, text_baseline,
335 text, n);
336 XCopyArea(dpy, prog_pixmap, prog_win, bg_gc, 0, 0, text_wd, inner_thick,
337 text_x, depth);
338 }
339
slider_update(double delta,Bool warp)340 void slider_update(double delta, Bool warp) {
341 XWindowChanges changes;
342 int old_pos = slider_pos;
343 slider_pos = clamp(pos_min, slider_pos + delta, pos_max);
344 if (warp)
345 XWarpPointer(dpy, None, None, 0, 0, 0, 0, slider_pos - old_pos, 0);
346 changes.x = slider_pos;
347 XConfigureWindow(dpy, slider_win, CWX, &changes);
348 prog_update((double)(slider_pos - pos_min) / (pos_max - pos_min), 1);
349 }
350
351 void mainloop(void);
352
main(int argc,char ** argv)353 int main(int argc, char **argv) {
354 XSetWindowAttributes attr;
355 XGCValues gc_values;
356
357 dpy = XOpenDisplay(0);
358 cmap = DefaultColormap(dpy, DefaultScreen(dpy));
359
360 shadow.red = (unsigned short)((double)(bg.red) * shade);
361 shadow.green = (unsigned short)((double)(bg.green) * shade);
362 shadow.blue = (unsigned short)((double)(bg.blue) * shade);
363
364 hilite.red = 65535 - (unsigned short)((65535.0-(double)(bg.red)) * shade);
365 hilite.green = 65535-(unsigned short)((65535.0-(double)(bg.green))*shade);
366 hilite.blue = 65535 - (unsigned short)((65535.0-(double)(bg.blue))*shade);
367
368 XAllocColor(dpy, cmap, &bg);
369 XAllocColor(dpy, cmap, &trough);
370 XAllocColor(dpy, cmap, &shadow);
371 XAllocColor(dpy, cmap, &hilite);
372 XAllocColor(dpy, cmap, &fill);
373
374 fontsize = (int)(font_frac * (double)thickness);
375 sprintf(buf, fontname, fontsize);
376 font = XLoadQueryFont(dpy, buf);
377
378 total_wd = 2 * padding + length;
379 base_wd = 2 * padding + 2 * depth + 4;
380 total_ht = 3 * padding + 2 * thickness;
381 base_ht = 3 * padding + 4 * depth + 3;
382
383 attr.cursor = XCreateFontCursor(dpy, cursor_id);
384 attr.background_pixel = bg.pixel;
385 attr.event_mask = StructureNotifyMask;
386 main_win = XCreateWindow(dpy, RootWindow(dpy, DefaultScreen(dpy)),
387 0, 0, total_wd, total_ht, 0,
388 CopyFromParent,CopyFromParent,CopyFromParent,
389 CWCursor|CWBackPixel|CWEventMask, &attr);
390
391 {
392 XSizeHints normal_hints;
393 XWMHints wm_hints;
394 XClassHint class_hints;
395 XTextProperty window_name, icon_name;
396 char *window_str = "Raw X Widgets (C Xlib)";
397 char *icon_str = "widgets";
398 normal_hints.min_width = normal_hints.base_width = base_wd;
399 normal_hints.min_height = normal_hints.base_height = base_ht;
400 normal_hints.min_aspect.x = 3; normal_hints.min_aspect.y = 2;
401 normal_hints.max_aspect.x = 1000; normal_hints.max_aspect.y = 1;
402 normal_hints.flags = PSize | PMinSize | PAspect | PBaseSize;
403 wm_hints.input = True;
404 wm_hints.initial_state = NormalState;
405 wm_hints.flags = InputHint | StateHint;
406 class_hints.res_name = argv[0];
407 class_hints.res_class = "widgets";
408 XStringListToTextProperty(&window_str, 1, &window_name);
409 XStringListToTextProperty(&icon_str, 1, &icon_name);
410 XSetWMProperties(dpy, main_win, &window_name, &icon_name, argv, argc,
411 &normal_hints, &wm_hints, &class_hints);
412 }
413
414 delete_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
415 XSetWMProtocols(dpy, main_win, &delete_atom, 1);
416
417 attr.background_pixel = trough.pixel;
418 attr.event_mask = ExposureMask;
419 prog_win = XCreateWindow(dpy, main_win, padding, padding,
420 length, thickness, 0,
421 CopyFromParent,CopyFromParent,CopyFromParent,
422 CWBackPixel|CWEventMask, &attr);
423
424 attr.background_pixel = trough.pixel;
425 attr.event_mask = ExposureMask;
426 sbar_win = XCreateWindow(dpy, main_win, padding, 2* padding + thickness,
427 length, thickness, 0,
428 CopyFromParent,CopyFromParent,CopyFromParent,
429 CWBackPixel|CWEventMask, &attr);
430
431 gc_values.foreground = bg.pixel;
432 bg_gc = XCreateGC(dpy, main_win, GCForeground, &gc_values);
433
434 gc_values.foreground = shadow.pixel;
435 shadow_gc = XCreateGC(dpy, main_win, GCForeground, &gc_values);
436
437 gc_values.foreground = hilite.pixel;
438 hilite_gc = XCreateGC(dpy, main_win, GCForeground, &gc_values);
439
440 inner_thick = thickness - 2 * depth;
441 slider_pos = depth;
442 pos_min = depth;
443 pos_max = length - thumb - depth - 2 * inner_thick;
444
445 attr.background_pixel = bg.pixel;
446 attr.event_mask = ExposureMask | ButtonPressMask | ButtonMotionMask
447 | PointerMotionHintMask;
448 slider_win = XCreateWindow(dpy, sbar_win, slider_pos, depth,
449 thumb + 2 * inner_thick, inner_thick, 0,
450 CopyFromParent,CopyFromParent,CopyFromParent,
451 CWBackPixel|CWEventMask, &attr);
452
453 attr.background_pixel = trough.pixel;
454 attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask;
455 lt_win = XCreateWindow(dpy, slider_win, 0, 0,
456 inner_thick, inner_thick, 0,
457 CopyFromParent,CopyFromParent,CopyFromParent,
458 CWBackPixel|CWEventMask, &attr);
459 rt_win = XCreateWindow(dpy, slider_win, thumb + inner_thick, 0,
460 inner_thick, inner_thick, 0,
461 CopyFromParent,CopyFromParent,CopyFromParent,
462 CWBackPixel|CWEventMask, &attr);
463
464 lt_state = rt_state = 0;
465
466 XMapWindow(dpy, lt_win);
467 XMapWindow(dpy, rt_win);
468 XMapWindow(dpy, slider_win);
469
470 text_wd = XTextWidth(font, "100%", 4) + 4 + 2;
471 text_x = (length - text_wd) / 2;
472 text_baseline = (thickness + font->ascent - font->descent) / 2 - depth;
473
474 prog_pixmap = XCreatePixmap(dpy, prog_win, text_wd, inner_thick,
475 DisplayPlanes(dpy, DefaultScreen(dpy)));
476
477 gc_values.foreground = trough.pixel;
478 gc_values.font = font->fid;
479 trough_gc = XCreateGC(dpy, main_win, GCForeground|GCFont, &gc_values);
480
481 gc_values.foreground = fill.pixel;
482 fill_gc = XCreateGC(dpy, main_win, GCForeground|GCFont, &gc_values);
483
484 XSetFont(dpy, shadow_gc, font->fid);
485 XSetFont(dpy, hilite_gc, font->fid);
486 XSetFont(dpy, bg_gc, font->fid);
487
488 font_height = font->ascent + font->descent;
489
490 XMapWindow(dpy, prog_win);
491 XMapWindow(dpy, sbar_win);
492 XMapWindow(dpy, main_win);
493
494 mainloop();
495 return 0;
496 }
497
mainloop(void)498 void mainloop(void) {
499 fd_set fds;
500 struct timeval timeout, short_time;
501
502 double slider_speed;
503 int pointer_pos, last_pos = -1;
504 int prog_dirty = 0, sbar_dirty = 0, slider_dirty = 0,
505 lt_dirty = 0, rt_dirty = 0;
506 int resize_pending = 0;
507 int x_fd = ConnectionNumber(dpy);
508 XEvent e;
509
510 FD_ZERO(&fds);
511 FD_SET(x_fd, &fds);
512 timeout.tv_sec = 0; timeout.tv_usec = 0;
513
514 /* Even though this program can probably handle events as fast as
515 the X server can generate them, it can't hurt to use some sort
516 of `flow control' to throw out excess events in case we're ever
517 behind. */
518
519 /* For pointer motion events, this is accomplished by selecting
520 PointerMotionHint on the slider (see above), so that the server
521 never sends a sequence of motion events -- instead, it sends
522 one, which we throw away but use as our cue to query the
523 pointer position. The query_pointer is then a sign to the
524 server that we'd be willing to accept one more event, and so
525 on. Notice that this requires at least one round trip between
526 the server and the client for each motion, which puts a limit
527 on performance. */
528
529 /* Expose and ConfigureNotify (resize) events have the same
530 problem, though it's only noticeable if your window manager
531 supports opaque window movement or opaque resize, respectively
532 (the latter is fairly rare in X, perhaps because average X
533 clients handle it fairly poorly; I for one am quite envious of
534 how smoothly windows resize in Windows NT). We can't do
535 anything to tell the server to only send us one of these
536 events, but the next best thing is to just ignore them until
537 there aren't any other events pending. (In some toolkits this
538 would be called `idle-loop' processing). It's always safe to
539 ignore intermediate resizes, but with expose events we can only
540 do this because we always redraw the whole window, instead of
541 just the newly-visible part. A more sophisticated approach
542 would keep track of the exposed region, either with a bounding
543 box or some more precise data structure, and then clip the
544 drawing to that (either client-side or using a clip mask in the
545 GC). */
546
547 for (;;) {
548 if (timeout.tv_usec) {
549 XFlush(dpy);
550 while (!select(x_fd + 1, &fds, 0, 0, &timeout)) {
551 FD_SET(x_fd, &fds);
552 slider_update(slider_speed, 1);
553 slider_speed += sign(slider_speed) * accel;
554 if (slider_pos == pos_min || slider_pos == pos_max) {
555 timeout.tv_sec = timeout.tv_usec = 0;
556 break;
557 } else {
558 timeout.tv_sec = 0; timeout.tv_usec = delay;
559 }
560 XFlush(dpy);
561 }
562 }
563 FD_SET(x_fd, &fds);
564 XFlush(dpy);
565 short_time.tv_sec = 0; short_time.tv_usec = 1000;
566 if (!select(x_fd + 1, &fds, 0, 0, &short_time)) {
567 if (resize_pending) {
568 XWindowChanges changes;
569
570 resize_pending = 0;
571 total_ht = max(total_ht, base_ht);
572 length = total_wd - 2 * padding;
573 thickness = (total_ht - 3 * padding + 1) / 2;
574 if (relief_frac)
575 depth = (int)(relief_frac * (double)thickness);
576 inner_thick = thickness - 2 * depth;
577 thumb = length / 3;
578 XResizeWindow(dpy, prog_win, length, thickness);
579 fontsize = (int)(font_frac * (double)thickness);
580 XFreeFont(dpy, font);
581 sprintf(buf, fontname, fontsize);
582 font = XLoadQueryFont(dpy, buf);
583 XSetFont(dpy, bg_gc, font->fid);
584 XSetFont(dpy, hilite_gc, font->fid);
585 XSetFont(dpy, shadow_gc, font->fid);
586
587 text_wd = XTextWidth(font, "100%", 4) + 4 + 2;
588 text_x = (length - text_wd) / 2;
589 text_baseline = (thickness + font->ascent
590 - font->descent) / 2 - depth;
591 font_height = font->ascent + font->descent;
592
593 XFreePixmap(dpy, prog_pixmap);
594 prog_pixmap =
595 XCreatePixmap(dpy, prog_win, text_wd, inner_thick,
596 DisplayPlanes(dpy, DefaultScreen(dpy)));
597 changes.y = 2 * padding + thickness;
598 changes.width = length; changes.height = thickness;
599 XConfigureWindow(dpy, sbar_win, CWY|CWWidth|CWHeight,
600 &changes);
601 pos_min = depth;
602 pos_max = length - thumb - depth - 2 * inner_thick;
603 slider_pos = pos_min
604 + (int)(frac * (double)(pos_max - pos_min));
605 XMoveResizeWindow(dpy, slider_win, slider_pos, depth,
606 thumb + 2 * inner_thick, inner_thick);
607 XResizeWindow(dpy, lt_win, inner_thick, inner_thick);
608 changes.x = thumb + inner_thick;
609 changes.width = changes.height = inner_thick;
610 XConfigureWindow(dpy, rt_win, CWX|CWWidth|CWHeight,
611 &changes);
612 }
613 if (prog_dirty) {
614 draw_slope(prog_win, 0, 0, length, thickness, prog_relief);
615 prog_update(frac, 0);
616 prog_dirty = 0;
617 }
618 if (sbar_dirty) {
619 draw_slope(sbar_win, 0, 0, length, thickness, sbar_relief);
620 sbar_dirty = 0;
621 }
622 if (slider_dirty) {
623 draw_slope(slider_win, inner_thick, 0, thumb,
624 inner_thick, slider_relief);
625 if (dimple)
626 paint_slope_circle(slider_win,
627 thumb / 2 +(int)((2.0 - dimple)/2.0
628 * (double)inner_thick),
629 (int)((1.0 - dimple)
630 * (double)inner_thick / 2.0),
631 (int)(dimple * (double)inner_thick),
632 depth, dimple_relief);
633 slider_dirty = 0;
634 }
635 if (lt_dirty) {
636 paint_arrow(lt_win, 0, 0, inner_thick - 1, 3,
637 arrow_relief ^ lt_state);
638 lt_dirty = 0;
639 }
640 if (rt_dirty) {
641 paint_arrow(rt_win, 0, 0, inner_thick - 1, 1,
642 arrow_relief ^ rt_state);
643 rt_dirty = 0;
644 }
645 }
646 XNextEvent(dpy, &e);
647 switch (e.type) {
648 case ClientMessage:
649 if (e.xclient.data.l[0] == delete_atom)
650 exit(0);
651 break;
652 case ConfigureNotify: {
653 int wd = e.xconfigure.width;
654 int ht = e.xconfigure.height;
655 if (wd != total_wd || ht != total_ht) {
656 resize_pending++;
657 total_wd = wd; total_ht = ht;
658 }
659 break; }
660 case Expose: {
661 Window win = e.xexpose.window;
662 if (win == sbar_win) {
663 if (e.xexpose.x < depth || e.xexpose.y < depth
664 || e.xexpose.x + e.xexpose.width > length - depth
665 || e.xexpose.y + e.xexpose.height > thickness - depth) {
666 /* In the scrollbar, we throw out exposures that
667 don't include the border (including all the
668 ones caused by moving the slider), since the
669 server fills the trough in with the window's
670 background color automatically. */
671 sbar_dirty++;
672 }
673 } else if (win == prog_win)
674 prog_dirty++;
675 else if (win == slider_win)
676 slider_dirty++;
677 else if (win == lt_win)
678 lt_dirty++;
679 else if (win == rt_win)
680 rt_dirty++;
681 break; }
682 case ButtonPress: {
683 Window win = e.xbutton.window;
684 if (win == slider_win) {
685 pointer_pos = slider_pos;
686 last_pos = e.xbutton.x_root;
687 } else if (win == lt_win) {
688 if (2*abs(e.xbutton.y - inner_thick / 2) <= e.xbutton.x) {
689 lt_state = arrow_change;
690 slider_update(-1, 1);
691 paint_arrow(lt_win, 0, 0, inner_thick - 1, 3,
692 arrow_relief ^ lt_state);
693 slider_speed = -1;
694 timeout.tv_sec = 0; timeout.tv_usec = initial_delay;
695 }
696 } else if (win == rt_win) {
697 if (2*abs(e.xbutton.y - inner_thick / 2)
698 <= inner_thick - e.xbutton.x)
699 {
700 rt_state = arrow_change;
701 slider_update(1, 1);
702 paint_arrow(rt_win, 0, 0, inner_thick - 1, 1,
703 arrow_relief ^ rt_state);
704 slider_speed = 1;
705 timeout.tv_sec = 0; timeout.tv_usec = initial_delay;
706 }
707 }
708 break; }
709 case MotionNotify:
710 if (e.xmotion.window == slider_win && last_pos != -1) {
711 int na, root_x;
712 Window NA;
713 XQueryPointer(dpy, slider_win, &NA, &NA, &root_x, &na,
714 &na, &na, (unsigned int *)&na);
715 pointer_pos += root_x - last_pos;
716 slider_update(pointer_pos - slider_pos, 0);
717 last_pos = root_x;
718 }
719 break;
720 case ButtonRelease: {
721 Window win = e.xbutton.window;
722 if (win == slider_win && last_pos != -1) {
723 slider_update(e.xbutton.x_root - last_pos, 0);
724 last_pos = -1;
725 } else if (win == lt_win) {
726 lt_state = 0;
727 paint_arrow(lt_win, 0, 0, inner_thick - 1, 3,
728 arrow_relief ^ lt_state);
729 timeout.tv_sec = 0; timeout.tv_usec = 0;
730 } else if (win == rt_win) {
731 rt_state = 0;
732 paint_arrow(rt_win, 0, 0, inner_thick - 1, 1,
733 arrow_relief ^ rt_state);
734 timeout.tv_sec = 0; timeout.tv_usec = 0;
735 }
736 break; }
737 }
738 }
739 }
740