wmpager: add missing Makefile.am
[dockapps.git] / wmix / ui_x.c
blob3d6432984d5b5074390c5f61d24eb2fe6fdf0d1b
1 /* WMix 3.0 -- a mixer using the OSS mixer API.
2 * Copyright (C) 2000, 2001
3 * Daniel Richard G. <skunk@mit.edu>,
4 * timecop <timecop@japan.co.jp>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <math.h>
31 #include <X11/X.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/Xatom.h>
35 #include <X11/extensions/shape.h>
36 #include <X11/xpm.h>
37 #include <X11/cursorfont.h>
39 #include "include/master.xpm"
40 #include "include/led-on.xpm"
41 #include "include/led-off.xpm"
43 #include "include/common.h"
44 #include "include/misc.h"
45 #include "include/mixer.h"
46 #include "include/ui_x.h"
48 #ifndef PI
49 #define PI M_PI
50 #endif
52 #define LED_POS_RADIUS 8
53 #define KNOB_CENTER_X 49
54 #define KNOB_CENTER_Y 48
55 #define LED_WIDTH 6
56 #define LED_HEIGHT 6
58 typedef struct _Dockapp Dockapp;
60 struct _Dockapp {
61 int width;
62 int height;
63 Pixmap pixmap;
64 Pixmap mask;
65 GC gc;
66 int ctlength;
68 Window osd;
69 GC osd_gc;
70 int osd_width;
71 bool osd_mapped;
75 extern Config config;
77 static Pixmap led_on_pixmap;
78 static Pixmap led_on_mask;
79 static Pixmap led_off_pixmap;
80 static Pixmap led_off_mask;
82 #define copy_xpm_area(x, y, w, h, dx, dy) \
83 XCopyArea(display, dockapp.pixmap, dockapp.pixmap, dockapp.gc, \
84 x, y, w, h, dx, dy)
86 /* local prototypes */
87 static Cursor create_null_cursor(Display *x_display);
89 /* ui stuff */
90 static void draw_stereo_led(void);
91 static void draw_rec_led(void);
92 static void draw_mute_led(void);
93 static void draw_percent(void);
94 static void draw_knob(float volume);
95 static void draw_slider(float offset);
97 /* global variables */
98 static Dockapp dockapp;
99 static Display *display;
100 static Window win;
101 static Window iconwin;
102 static Cursor hand_cursor;
103 static Cursor null_cursor;
104 static Cursor norm_cursor;
105 static Cursor bar_cursor;
107 /* public methods */
108 void dockapp_init(Display *x_display)
110 display = x_display;
113 void redraw_window(void)
115 XCopyArea(display, dockapp.pixmap, iconwin, dockapp.gc,
116 0, 0, dockapp.width, dockapp.height, 0, 0);
117 XCopyArea(display, dockapp.pixmap, win, dockapp.gc,
118 0, 0, dockapp.width, dockapp.height, 0, 0);
121 void ui_update(void)
123 draw_stereo_led();
124 draw_rec_led();
125 draw_mute_led();
126 draw_knob(mixer_get_volume());
127 draw_slider(mixer_get_balance());
128 redraw_window();
131 void knob_turn(float delta)
133 mixer_set_volume_rel(delta);
134 draw_knob(mixer_get_volume());
135 redraw_window();
138 void slider_move(float delta)
140 mixer_set_balance_rel(delta);
141 draw_slider(mixer_get_balance());
142 redraw_window();
145 int blit_string(const char *text)
147 register int i;
148 register int c;
149 register int k;
151 k = 0;
152 copy_xpm_area(0, 87, 256, 9, 0, 96);
154 for (i = 0; text[i] || i > 31; i++) {
155 c = toupper(text[i]);
156 if (c == '-') {
157 copy_xpm_area(60, 67, 6, 8, k, 96);
158 k += 6;
160 if (c == ' ') {
161 copy_xpm_area(66, 67, 6, 8, k, 96);
162 k += 6;
164 if (c == '.') {
165 copy_xpm_area(72, 67, 6, 8, k, 96);
166 k += 6;
168 if (c >= 'A' && c <= 'Z') { /* letter */
169 c = c - 'A';
170 copy_xpm_area(c * 6, 77, 6, 8, k, 96);
171 k += 6;
172 } else if (c >= '0' && c <= '9') { /* number */
173 c = c - '0';
174 copy_xpm_area(c * 6, 67, 6, 8, k, 96);
175 k += 6;
178 dockapp.ctlength = k;
179 return k;
182 void scroll_text(int x, int y, int width, bool reset)
184 static int pos;
185 static int first;
186 static int stop;
188 /* no text scrolling at all */
189 if (!config.scrolltext) {
190 if (!reset)
191 return;
192 copy_xpm_area(0, 96, 58, 9, x, y);
193 redraw_window();
194 return;
197 if (reset) {
198 pos = 0;
199 first = 0;
200 stop = 0;
201 copy_xpm_area(0, 87, width, 9, x, y);
204 if (stop) {
205 return;
208 if ((first == 0) && pos == 0) {
209 pos = width;
210 first = 1;
213 if (pos < -(dockapp.ctlength)) {
214 first = 1;
215 pos = width;
216 stop = 1;
217 return;
219 pos -= 2;
221 if (pos > 0) {
222 copy_xpm_area(0, 87, pos, 9, x, y); /* clear */
223 copy_xpm_area(0, 96, width - pos, 9, x + pos, y);
224 } else { /* don't need to clear, already in text */
225 copy_xpm_area(abs(pos), 96, width, 9, x, y);
227 redraw_window();
228 return;
231 void new_window(char *name, int width, int height)
233 XpmAttributes attr;
234 Pixel fg, bg;
235 XGCValues gcval;
236 XSizeHints sizehints;
237 XClassHint classhint;
238 XWMHints wmhints;
239 XTextProperty wname;
241 dockapp.width = width;
242 dockapp.height = height;
244 sizehints.flags = USSize | USPosition;
245 sizehints.x = 0;
246 sizehints.y = 0;
247 sizehints.width = width;
248 sizehints.height = height;
250 fg = BlackPixel(display, DefaultScreen(display));
251 bg = WhitePixel(display, DefaultScreen(display));
253 win = XCreateSimpleWindow(display, DefaultRootWindow(display),
254 sizehints.x, sizehints.y,
255 sizehints.width, sizehints.height, 1, fg,
256 bg);
258 iconwin = XCreateSimpleWindow(display, win, sizehints.x, sizehints.y,
259 sizehints.width, sizehints.height, 1, fg,
260 bg);
262 XSetWMNormalHints(display, win, &sizehints);
263 classhint.res_name = name;
264 classhint.res_class = name;
265 XSetClassHint(display, win, &classhint);
267 #define INPUT_MASK \
268 ButtonPressMask \
269 | ExposureMask \
270 | ButtonReleaseMask \
271 | PointerMotionMask \
272 | LeaveWindowMask \
273 | StructureNotifyMask
275 XSelectInput(display, win, INPUT_MASK);
276 XSelectInput(display, iconwin, INPUT_MASK);
278 #undef INPUT_MASk
280 XStringListToTextProperty(&name, 1, &wname);
281 XSetWMName(display, win, &wname);
283 gcval.foreground = fg;
284 gcval.background = bg;
285 gcval.graphics_exposures = 0;
287 dockapp.gc =
288 XCreateGC(display, win,
289 GCForeground | GCBackground | GCGraphicsExposures,
290 &gcval);
292 attr.exactColors = 0;
293 attr.alloc_close_colors = 1;
294 attr.closeness = 30000;
295 attr.valuemask = (XpmExactColors | XpmAllocCloseColors | XpmCloseness);
296 if ((XpmCreatePixmapFromData(display, DefaultRootWindow(display),
297 master_xpm, &dockapp.pixmap, &dockapp.mask,
298 &attr) != XpmSuccess) ||
299 (XpmCreatePixmapFromData(display, DefaultRootWindow(display),
300 led_on_xpm, &led_on_pixmap, &led_on_mask,
301 &attr) != XpmSuccess) ||
302 (XpmCreatePixmapFromData(display, DefaultRootWindow(display),
303 led_off_xpm, &led_off_pixmap, &led_off_mask,
304 &attr) != XpmSuccess)) {
305 fputs("Cannot allocate colors for the dockapp pixmaps!\n", stderr);
306 exit(EXIT_FAILURE);
309 XShapeCombineMask(display, win, ShapeBounding, 0, 0, dockapp.mask,
310 ShapeSet);
311 XShapeCombineMask(display, iconwin, ShapeBounding, 0, 0, dockapp.mask,
312 ShapeSet);
314 wmhints.initial_state = WithdrawnState;
315 wmhints.icon_window = iconwin;
316 wmhints.icon_x = sizehints.x;
317 wmhints.icon_y = sizehints.y;
318 wmhints.window_group = win;
319 wmhints.flags =
320 StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
321 XSetWMHints(display, win, &wmhints);
323 hand_cursor = XCreateFontCursor(display, XC_hand2);
324 norm_cursor = XCreateFontCursor(display, XC_left_ptr);
325 bar_cursor = XCreateFontCursor(display, XC_sb_up_arrow);
326 null_cursor = create_null_cursor(display);
328 XMapWindow(display, win);
331 void new_osd(int width, int height)
333 Window osd;
334 Pixel fg, bg;
335 XGCValues gcval;
336 GC gc;
337 XSizeHints sizehints;
338 XSetWindowAttributes xattributes;
339 int win_layer = 6;
340 XFontStruct *fs = NULL;
342 sizehints.flags = USSize | USPosition;
343 sizehints.x = (DisplayWidth(display, 0) - width) / 2;
344 sizehints.y = (DisplayHeight(display, 0) - 120);
345 sizehints.width = width;
346 sizehints.height = height;
347 xattributes.save_under = True;
348 xattributes.override_redirect = True;
349 xattributes.cursor = None;
352 fg = WhitePixel(display, DefaultScreen(display));
353 bg = BlackPixel(display, DefaultScreen(display));
355 osd = XCreateSimpleWindow(display, DefaultRootWindow(display),
356 sizehints.x, sizehints.y, width, height,
357 0, fg, bg);
359 XSetWMNormalHints(display, osd, &sizehints);
360 XChangeWindowAttributes(display, osd, CWSaveUnder | CWOverrideRedirect,
361 &xattributes);
362 XStoreName(display, osd, "osd");
363 XSelectInput(display, osd, ExposureMask);
365 XChangeProperty(display, osd, XInternAtom(display, "_WIN_LAYER", False),
366 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&win_layer, 1);
368 gcval.foreground = get_color(display, config.osd_color);
369 gcval.background = bg;
370 gcval.graphics_exposures = 0;
373 * -sony-fixed-medium-r-normal--24-170-100-100-c-120-iso8859-1
374 * -misc-fixed-medium-r-normal--36-*-75-75-c-*-iso8859-* */
376 /* try our cool scaled 36pt fixed font */
377 fs = XLoadQueryFont(display,
378 "-misc-fixed-medium-r-normal--36-*-75-75-c-*-iso8859-*");
380 if (fs == NULL) {
381 /* they don't have it! */
382 /* try our next preferred font (100dpi sony) */
383 fprintf(stderr, "Trying alternate font\n");
384 fs = XLoadQueryFont(display,
385 "-sony-fixed-medium-r-normal--24-*-100-100-c-*-iso8859-*");
387 /* they don't have the sony font either */
388 if (fs == NULL) {
389 fprintf(stderr, "Trying \"fixed\" font\n");
390 fs = XLoadQueryFont(display,
391 "fixed");
392 /* if they don't have the fixed font, we've got different kind
393 * of problems */
394 if (fs == NULL) {
395 fprintf(stderr, "Your X server is probably broken\n");
396 exit(1);
401 gc =
402 XCreateGC(display, osd,
403 GCForeground | GCBackground | GCGraphicsExposures,
404 &gcval);
405 XSetFont(display, gc, fs->fid);
407 dockapp.osd = osd;
408 dockapp.osd_gc = gc;
409 dockapp.osd_width = width;
410 dockapp.osd_mapped = false;
413 void update_osd(float volume, bool up)
415 int i;
416 int foo;
417 static int bar;
419 if (config.osd) {
420 foo =
421 (((dockapp.osd_width / 100) * (volume * 100)) / 20) + 1;
423 if ((foo != bar) || up) {
424 XClearArea(display, dockapp.osd, ((bar - 1) * 20), 30,
425 (foo * 20), 25, 1);
426 for (i = 1; i < foo; i++)
427 XFillRectangle(display, dockapp.osd, dockapp.osd_gc,
428 i * 20, 30, 5, 25);
430 bar = foo;
434 void unmap_osd(void)
436 if (config.osd) {
437 XUnmapWindow(display, dockapp.osd);
438 XFlush(display);
439 dockapp.osd_mapped = false;
443 void map_osd(void)
445 if (config.osd) {
446 XMapRaised(display, dockapp.osd);
447 XDrawString(display, dockapp.osd, dockapp.osd_gc, 1, 25,
448 mixer_get_channel_name(), strlen(mixer_get_channel_name()));
449 update_osd(mixer_get_volume(), true);
450 XFlush(display);
451 dockapp.osd_mapped = true;
455 bool osd_mapped(void)
457 return dockapp.osd_mapped;
460 void set_cursor(int type)
462 static int oldtype;
464 if (oldtype == type)
465 return;
467 switch (type) {
468 case NULL_CURSOR:
469 XDefineCursor(display, win, null_cursor);
470 XDefineCursor(display, iconwin, null_cursor);
471 break;
472 case NORMAL_CURSOR:
473 XDefineCursor(display, win, norm_cursor);
474 XDefineCursor(display, iconwin, norm_cursor);
475 break;
476 case HAND_CURSOR:
477 XDefineCursor(display, win, hand_cursor);
478 XDefineCursor(display, iconwin, hand_cursor);
479 break;
480 case BAR_CURSOR:
481 XDefineCursor(display, win, bar_cursor);
482 XDefineCursor(display, iconwin, bar_cursor);
483 break;
485 oldtype = type;
488 /* private */
489 static void draw_stereo_led(void)
491 if (mixer_is_stereo()) /* stereo capable */
492 copy_xpm_area(78, 0, 9, 7, 28, 14); /* light up LCD */
493 else /* mono channel */
494 copy_xpm_area(78, 7, 9, 7, 28, 14); /* turn off LCD */
497 static void draw_rec_led(void)
499 if (mixer_is_rec()) /* record enabled */
500 copy_xpm_area(65, 0, 13, 7, 4, 14); /* Light up LCD */
501 else /* record disabled */
502 copy_xpm_area(65, 7, 13, 7, 4, 14); /* turn off LCD */
505 static void draw_mute_led(void)
507 if (mixer_is_muted()) /* mute */
508 copy_xpm_area(65, 14, 20, 7, 39, 14); /* light up LCD */
509 else /* unmute */
510 copy_xpm_area(65, 21, 20, 7, 39, 14); /* turn off LCD */
513 static void draw_percent(void)
515 int volume = (int)(mixer_get_volume() * 100);
517 copy_xpm_area(0, 87, 18, 9, 41, 22); /* clear percentage */
519 if (volume < 100) {
520 if (volume >= 10)
521 copy_xpm_area((volume / 10) * 6, 67, 6, 9, 47, 22);
522 copy_xpm_area((volume % 10) * 6, 67, 6, 9, 53, 22);
523 } else {
524 copy_xpm_area(6, 67, 6, 9, 41, 22);
525 copy_xpm_area(0, 67, 6, 9, 47, 22);
526 copy_xpm_area(0, 67, 6, 9, 53, 22);
530 static void draw_knob(float volume)
532 float bearing, led_x, led_y;
533 int led_topleft_x, led_topleft_y;
534 Pixmap led_pixmap, led_mask;
536 bearing = (1.25 * PI) - (1.5 * PI) * volume;
538 led_x = KNOB_CENTER_X + LED_POS_RADIUS * cos(bearing);
539 led_y = KNOB_CENTER_Y - LED_POS_RADIUS * sin(bearing);
541 led_topleft_x = (int)(led_x - (LED_WIDTH / 2.0) + 0.5);
542 led_topleft_y = (int)(led_y - (LED_HEIGHT / 2.0) + 0.5);
544 /* clear previous knob picture */
545 copy_xpm_area(87, 0, 26, 26, 36, 35);
547 if (mixer_is_muted()) {
548 led_pixmap = led_off_pixmap;
549 led_mask = led_off_mask;
550 } else {
551 led_pixmap = led_on_pixmap;
552 led_mask = led_on_mask;
554 XCopyArea(display, led_pixmap, dockapp.pixmap, dockapp.gc,
555 0, 0, LED_WIDTH, LED_HEIGHT, led_topleft_x, led_topleft_y);
556 draw_percent();
559 static void draw_slider(float offset)
561 int x = (offset * 50) / 5;
563 copy_xpm_area(65, 45, 27, 20, 4, 40); /* repair region. move */
564 copy_xpm_area(65, 29, 7, 15, 14 + x, 43); /* slider */
567 static Cursor create_null_cursor(Display *x_display)
569 Pixmap cursor_mask;
570 XGCValues gcval;
571 GC gc;
572 XColor dummy_color;
573 Cursor cursor;
575 cursor_mask = XCreatePixmap(x_display, DefaultRootWindow(x_display), 1, 1, 1);
576 gcval.function = GXclear;
577 gc = XCreateGC(x_display, cursor_mask, GCFunction, &gcval);
578 XFillRectangle(x_display, cursor_mask, gc, 0, 0, 1, 1);
579 dummy_color.pixel = 0;
580 dummy_color.red = 0;
581 dummy_color.flags = 04;
582 cursor = XCreatePixmapCursor(x_display, cursor_mask, cursor_mask,
583 &dummy_color, &dummy_color, 0, 0);
584 XFreePixmap(x_display, cursor_mask);
585 XFreeGC(x_display, gc);
587 return cursor;
590 unsigned long get_color(Display *display, char *color_name)
592 XColor color;
593 XWindowAttributes winattr;
595 XGetWindowAttributes(display,
596 RootWindow(display, DefaultScreen(display)), &winattr);
598 color.pixel = 0;
599 XParseColor(display, winattr.colormap, color_name, &color);
601 color.flags = DoRed | DoGreen | DoBlue;
602 XAllocColor(display, winattr.colormap, &color);
604 return color.pixel;