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.
33 #include <X11/Xutil.h>
34 #include <X11/Xatom.h>
35 #include <X11/extensions/shape.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"
47 #include "include/config.h"
54 #define LED_POS_RADIUS 8
55 #define KNOB_CENTER_X 49
56 #define KNOB_CENTER_Y 48
60 typedef struct _Dockapp Dockapp
;
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, \
86 /* local prototypes */
87 static Cursor
create_null_cursor(Display
*x_display
);
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
;
101 static Window iconwin
;
102 static Cursor hand_cursor
;
103 static Cursor null_cursor
;
104 static Cursor norm_cursor
;
105 static Cursor bar_cursor
;
108 void dockapp_init(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);
126 draw_knob(mixer_get_volume());
127 draw_slider(mixer_get_balance());
131 void knob_turn(float delta
)
133 mixer_set_volume_rel(delta
);
134 draw_knob(mixer_get_volume());
138 void slider_move(float delta
)
140 mixer_set_balance_rel(delta
);
141 draw_slider(mixer_get_balance());
145 int blit_string(const char *text
)
152 copy_xpm_area(0, 87, 256, 9, 0, 96);
154 for (i
= 0; text
[i
] || i
> 31; i
++) {
155 c
= toupper(text
[i
]);
157 copy_xpm_area(60, 67, 6, 8, k
, 96);
161 copy_xpm_area(66, 67, 6, 8, k
, 96);
165 copy_xpm_area(72, 67, 6, 8, k
, 96);
168 if (c
>= 'A' && c
<= 'Z') { /* letter */
170 copy_xpm_area(c
* 6, 77, 6, 8, k
, 96);
172 } else if (c
>= '0' && c
<= '9') { /* number */
174 copy_xpm_area(c
* 6, 67, 6, 8, k
, 96);
178 dockapp
.ctlength
= k
;
182 void scroll_text(int x
, int y
, int width
, bool reset
)
188 /* no text scrolling at all */
189 if (!config
.scrolltext
) {
192 copy_xpm_area(0, 96, 58, 9, x
, y
);
201 copy_xpm_area(0, 87, width
, 9, x
, y
);
208 if ((first
== 0) && pos
== 0) {
213 if (pos
< -(dockapp
.ctlength
)) {
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
);
231 void new_window(char *name
, int width
, int height
)
236 XSizeHints sizehints
;
237 XClassHint classhint
;
241 dockapp
.width
= width
;
242 dockapp
.height
= height
;
244 sizehints
.flags
= USSize
| USPosition
;
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
,
258 iconwin
= XCreateSimpleWindow(display
, win
, sizehints
.x
, sizehints
.y
,
259 sizehints
.width
, sizehints
.height
, 1, fg
,
262 XSetWMNormalHints(display
, win
, &sizehints
);
263 classhint
.res_name
= name
;
264 classhint
.res_class
= name
;
265 XSetClassHint(display
, win
, &classhint
);
270 | ButtonReleaseMask \
271 | PointerMotionMask \
273 | StructureNotifyMask
275 XSelectInput(display
, win
, INPUT_MASK
);
276 XSelectInput(display
, iconwin
, 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;
288 XCreateGC(display
, win
,
289 GCForeground
| GCBackground
| GCGraphicsExposures
,
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
);
309 XShapeCombineMask(display
, win
, ShapeBounding
, 0, 0, dockapp
.mask
,
311 XShapeCombineMask(display
, iconwin
, ShapeBounding
, 0, 0, dockapp
.mask
,
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
;
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
)
337 XSizeHints sizehints
;
338 XSetWindowAttributes xattributes
;
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
,
359 XSetWMNormalHints(display
, osd
, &sizehints
);
360 XChangeWindowAttributes(display
, osd
, CWSaveUnder
| CWOverrideRedirect
,
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-*");
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 */
389 fprintf(stderr
, "Trying \"fixed\" font\n");
390 fs
= XLoadQueryFont(display
,
392 /* if they don't have the fixed font, we've got different kind
395 fprintf(stderr
, "Your X server is probably broken\n");
402 XCreateGC(display
, osd
,
403 GCForeground
| GCBackground
| GCGraphicsExposures
,
405 XSetFont(display
, gc
, fs
->fid
);
409 dockapp
.osd_width
= width
;
410 dockapp
.osd_mapped
= false;
413 void update_osd(float volume
, bool up
)
421 (((dockapp
.osd_width
/ 100) * (volume
* 100)) / 20) + 1;
423 if ((foo
!= bar
) || up
) {
424 XClearArea(display
, dockapp
.osd
, ((bar
- 1) * 20), 30,
426 for (i
= 1; i
< foo
; i
++)
427 XFillRectangle(display
, dockapp
.osd
, dockapp
.osd_gc
,
437 XUnmapWindow(display
, dockapp
.osd
);
439 dockapp
.osd_mapped
= false;
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);
451 dockapp
.osd_mapped
= true;
455 bool osd_mapped(void)
457 return dockapp
.osd_mapped
;
460 void set_cursor(int type
)
469 XDefineCursor(display
, win
, null_cursor
);
470 XDefineCursor(display
, iconwin
, null_cursor
);
473 XDefineCursor(display
, win
, norm_cursor
);
474 XDefineCursor(display
, iconwin
, norm_cursor
);
477 XDefineCursor(display
, win
, hand_cursor
);
478 XDefineCursor(display
, iconwin
, hand_cursor
);
481 XDefineCursor(display
, win
, bar_cursor
);
482 XDefineCursor(display
, iconwin
, bar_cursor
);
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 */
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 */
521 copy_xpm_area((volume
/ 10) * 6, 67, 6, 9, 47, 22);
522 copy_xpm_area((volume
% 10) * 6, 67, 6, 9, 53, 22);
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
;
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
;
550 led_pixmap
= led_on_pixmap
;
552 XCopyArea(display
, led_pixmap
, dockapp
.pixmap
, dockapp
.gc
,
553 0, 0, LED_WIDTH
, LED_HEIGHT
, led_topleft_x
, led_topleft_y
);
557 static void draw_slider(float offset
)
559 int x
= (offset
* 50) / 5;
561 copy_xpm_area(65, 45, 27, 20, 4, 40); /* repair region. move */
562 copy_xpm_area(65, 29, 7, 15, 14 + x
, 43); /* slider */
565 static Cursor
create_null_cursor(Display
*x_display
)
573 cursor_mask
= XCreatePixmap(x_display
, DefaultRootWindow(x_display
), 1, 1, 1);
574 gcval
.function
= GXclear
;
575 gc
= XCreateGC(x_display
, cursor_mask
, GCFunction
, &gcval
);
576 XFillRectangle(x_display
, cursor_mask
, gc
, 0, 0, 1, 1);
577 dummy_color
.pixel
= 0;
579 dummy_color
.flags
= 04;
580 cursor
= XCreatePixmapCursor(x_display
, cursor_mask
, cursor_mask
,
581 &dummy_color
, &dummy_color
, 0, 0);
582 XFreePixmap(x_display
, cursor_mask
);
583 XFreeGC(x_display
, gc
);
588 unsigned long get_color(Display
*display
, char *color_name
)
591 XWindowAttributes winattr
;
594 XGetWindowAttributes(display
,
595 RootWindow(display
, DefaultScreen(display
)), &winattr
);
597 status
= XParseColor(display
, winattr
.colormap
, color_name
, &color
);
599 fprintf(stderr
, "wmix:warning: Could not get color \"%s\" for OSD, falling back to default\n", color_name
);
601 if (color_name
!= default_osd_color
)
602 status
= XParseColor(display
, winattr
.colormap
, default_osd_color
, &color
);
604 return WhitePixel(display
, DefaultScreen(display
));
607 color
.flags
= DoRed
| DoGreen
| DoBlue
;
608 XAllocColor(display
, winattr
.colormap
, &color
);