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>
38 #include <X11/extensions/Xrandr.h>
40 #include "include/master.xpm"
41 #include "include/led-on.xpm"
42 #include "include/led-off.xpm"
44 #include "include/common.h"
45 #include "include/misc.h"
46 #include "include/mixer.h"
47 #include "include/ui_x.h"
48 #include "include/config.h"
55 #define LED_POS_RADIUS 8
56 #define KNOB_CENTER_X 49
57 #define KNOB_CENTER_Y 48
61 typedef struct _Dockapp Dockapp
;
80 static Pixmap led_on_pixmap
;
81 static Pixmap led_on_mask
;
82 static Pixmap led_off_pixmap
;
83 static Pixmap led_off_mask
;
85 #define copy_xpm_area(x, y, w, h, dx, dy) \
86 XCopyArea(display, dockapp.pixmap, dockapp.pixmap, dockapp.gc, \
89 /* local prototypes */
90 static Cursor
create_null_cursor(Display
*x_display
);
93 static void draw_stereo_led(void);
94 static void draw_rec_led(void);
95 static void draw_mute_led(void);
96 static void draw_percent(float volume
);
97 static void draw_knob(float volume
);
98 static void draw_slider(float offset
);
100 /* global variables */
101 static Dockapp dockapp
;
102 static Display
*display
;
104 static Window iconwin
;
105 static Cursor hand_cursor
;
106 static Cursor null_cursor
;
107 static Cursor norm_cursor
;
108 static Cursor bar_cursor
;
109 static Bool have_randr
;
112 void dockapp_init(Display
*x_display
, Bool randr
)
120 void redraw_window(void)
122 XCopyArea(display
, dockapp
.pixmap
, iconwin
, dockapp
.gc
,
123 0, 0, dockapp
.width
, dockapp
.height
, 0, 0);
124 XCopyArea(display
, dockapp
.pixmap
, win
, dockapp
.gc
,
125 0, 0, dockapp
.width
, dockapp
.height
, 0, 0);
133 draw_knob(mixer_get_volume());
134 draw_slider(mixer_get_balance());
138 void knob_turn(float delta
)
140 mixer_set_volume_rel(delta
);
141 draw_knob(mixer_get_volume());
145 void slider_move(float delta
)
147 mixer_set_balance_rel(delta
);
148 draw_slider(mixer_get_balance());
152 int blit_string(const char *text
)
159 copy_xpm_area(0, 87, 256, 9, 0, 96);
161 for (i
= 0; text
[i
] || i
> 31; i
++) {
162 c
= toupper(text
[i
]);
164 copy_xpm_area(60, 67, 6, 8, k
, 96);
168 copy_xpm_area(66, 67, 6, 8, k
, 96);
172 copy_xpm_area(72, 67, 6, 8, k
, 96);
175 if (c
>= 'A' && c
<= 'Z') { /* letter */
177 copy_xpm_area(c
* 6, 77, 6, 8, k
, 96);
179 } else if (c
>= '0' && c
<= '9') { /* number */
181 copy_xpm_area(c
* 6, 67, 6, 8, k
, 96);
185 dockapp
.ctlength
= k
;
189 void scroll_text(int x
, int y
, int width
, bool reset
)
195 /* no text scrolling at all */
196 if (!config
.scrolltext
) {
199 copy_xpm_area(0, 96, 58, 9, x
, y
);
208 copy_xpm_area(0, 87, width
, 9, x
, y
);
215 if ((first
== 0) && pos
== 0) {
220 if (pos
< -(dockapp
.ctlength
)) {
229 copy_xpm_area(0, 87, pos
, 9, x
, y
); /* clear */
230 copy_xpm_area(0, 96, width
- pos
, 9, x
+ pos
, y
);
231 } else { /* don't need to clear, already in text */
232 copy_xpm_area(abs(pos
), 96, width
, 9, x
, y
);
238 void new_window(char *name
, int width
, int height
, int argc
, char **argv
)
243 XSizeHints sizehints
;
244 XClassHint classhint
;
248 dockapp
.width
= width
;
249 dockapp
.height
= height
;
251 sizehints
.flags
= USSize
| USPosition
;
254 sizehints
.width
= width
;
255 sizehints
.height
= height
;
257 fg
= BlackPixel(display
, DefaultScreen(display
));
258 bg
= WhitePixel(display
, DefaultScreen(display
));
260 win
= XCreateSimpleWindow(display
, DefaultRootWindow(display
),
261 sizehints
.x
, sizehints
.y
,
262 sizehints
.width
, sizehints
.height
, 1, fg
,
265 iconwin
= XCreateSimpleWindow(display
, win
, sizehints
.x
, sizehints
.y
,
266 sizehints
.width
, sizehints
.height
, 1, fg
,
269 XSetWMNormalHints(display
, win
, &sizehints
);
270 classhint
.res_name
= name
;
271 classhint
.res_class
= name
;
272 XSetClassHint(display
, win
, &classhint
);
277 | ButtonReleaseMask \
278 | PointerMotionMask \
280 | StructureNotifyMask
282 XSelectInput(display
, win
, INPUT_MASK
);
283 XSelectInput(display
, iconwin
, INPUT_MASK
);
287 XStringListToTextProperty(&name
, 1, &wname
);
288 XSetWMName(display
, win
, &wname
);
290 gcval
.foreground
= fg
;
291 gcval
.background
= bg
;
292 gcval
.graphics_exposures
= 0;
295 XCreateGC(display
, win
,
296 GCForeground
| GCBackground
| GCGraphicsExposures
,
299 attr
.exactColors
= 0;
300 attr
.alloc_close_colors
= 1;
301 attr
.closeness
= 30000;
302 attr
.valuemask
= (XpmExactColors
| XpmAllocCloseColors
| XpmCloseness
);
303 if ((XpmCreatePixmapFromData(display
, DefaultRootWindow(display
),
304 master_xpm
, &dockapp
.pixmap
, &dockapp
.mask
,
305 &attr
) != XpmSuccess
) ||
306 (XpmCreatePixmapFromData(display
, DefaultRootWindow(display
),
307 led_on_xpm
, &led_on_pixmap
, &led_on_mask
,
308 &attr
) != XpmSuccess
) ||
309 (XpmCreatePixmapFromData(display
, DefaultRootWindow(display
),
310 led_off_xpm
, &led_off_pixmap
, &led_off_mask
,
311 &attr
) != XpmSuccess
)) {
312 fputs("Cannot allocate colors for the dockapp pixmaps!\n", stderr
);
316 XShapeCombineMask(display
, win
, ShapeBounding
, 0, 0, dockapp
.mask
,
318 XShapeCombineMask(display
, iconwin
, ShapeBounding
, 0, 0, dockapp
.mask
,
321 wmhints
.initial_state
= WithdrawnState
;
322 wmhints
.icon_window
= iconwin
;
323 wmhints
.icon_x
= sizehints
.x
;
324 wmhints
.icon_y
= sizehints
.y
;
325 wmhints
.window_group
= win
;
327 StateHint
| IconWindowHint
| IconPositionHint
| WindowGroupHint
;
328 XSetWMHints(display
, win
, &wmhints
);
330 hand_cursor
= XCreateFontCursor(display
, XC_hand2
);
331 norm_cursor
= XCreateFontCursor(display
, XC_left_ptr
);
332 bar_cursor
= XCreateFontCursor(display
, XC_sb_up_arrow
);
333 null_cursor
= create_null_cursor(display
);
335 XSetCommand(display
, win
, argv
, argc
);
337 XMapWindow(display
, win
);
340 XRRCrtcInfo
*crtc_info_by_output_name(char *monitor
)
342 XRRScreenResources
*screen
= XRRGetScreenResources(display
, DefaultRootWindow(display
));
343 for (int i
= 0; i
< screen
->noutput
; i
++) {
344 XRROutputInfo
*output_info
= XRRGetOutputInfo(display
, screen
, screen
->outputs
[i
]);
345 if (!strcmp(monitor
, output_info
->name
)) {
346 XRRCrtcInfo
*crtc_info
= XRRGetCrtcInfo(display
, screen
, output_info
->crtc
);
347 XRRFreeOutputInfo(output_info
);
348 XRRFreeScreenResources(screen
);
351 XRRFreeOutputInfo(output_info
);
353 XRRFreeScreenResources(screen
);
357 XRRCrtcInfo
*crtc_info_by_output_number(int monitor
)
359 XRRScreenResources
*screen
= XRRGetScreenResources(display
, DefaultRootWindow(display
));
360 if (monitor
>= screen
->ncrtc
) {
361 fprintf(stderr
, "wmix:warning: Requested osd monitor number is out of range, clamping\n");
362 monitor
= screen
->ncrtc
- 1;
364 XRRCrtcInfo
*crtc_info
= XRRGetCrtcInfo(display
, screen
, screen
->crtcs
[monitor
]);
365 XRRFreeScreenResources(screen
);
369 void new_osd(int height
)
375 XSizeHints sizehints
;
376 XSetWindowAttributes xattributes
;
378 XFontStruct
*fs
= NULL
;
379 XRRCrtcInfo
*crtc_info
;
385 if (config
.osd_monitor_name
) {
386 crtc_info
= crtc_info_by_output_name(config
.osd_monitor_name
);
387 if (crtc_info
== NULL
) {
388 fprintf(stderr
, "wmix:warning: Requested osd monitor not found, falling back to default\n");
389 crtc_info
= crtc_info_by_output_number(0);
393 crtc_info
= crtc_info_by_output_number(config
.osd_monitor_number
);
396 width
= crtc_info
->width
- 200;
397 x
= crtc_info
->x
+ 100;
398 y
= crtc_info
->y
+ crtc_info
->height
- 120;
399 XRRFreeCrtcInfo(crtc_info
);
401 width
== dockapp
.osd_width
&&
402 x
== dockapp
.osd_x
&&
403 y
== dockapp
.osd_y
) {
404 // Nothing important has changed.
408 width
= DisplayWidth(display
, DefaultScreen(display
)) - 200;
410 y
= DisplayHeight(display
, 0) - 120;
413 sizehints
.flags
= USSize
| USPosition
;
416 sizehints
.width
= width
;
417 sizehints
.height
= height
;
418 xattributes
.save_under
= True
;
419 xattributes
.override_redirect
= True
;
420 xattributes
.cursor
= None
;
422 fg
= WhitePixel(display
, DefaultScreen(display
));
423 bg
= BlackPixel(display
, DefaultScreen(display
));
426 XDestroyWindow(display
, dockapp
.osd
);
427 osd
= XCreateSimpleWindow(display
, DefaultRootWindow(display
),
428 sizehints
.x
, sizehints
.y
, width
, height
,
431 XSetWMNormalHints(display
, osd
, &sizehints
);
432 XChangeWindowAttributes(display
, osd
, CWSaveUnder
| CWOverrideRedirect
,
434 XStoreName(display
, osd
, "osd");
435 XSelectInput(display
, osd
, ExposureMask
);
437 XChangeProperty(display
, osd
, XInternAtom(display
, "_WIN_LAYER", False
),
438 XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *)&win_layer
, 1);
440 gcval
.foreground
= get_color(display
, config
.osd_color
);
441 gcval
.background
= bg
;
442 gcval
.graphics_exposures
= 0;
445 * -sony-fixed-medium-r-normal--24-170-100-100-c-120-iso8859-1
446 * -misc-fixed-medium-r-normal--36-*-75-75-c-*-iso8859-* */
448 /* try our cool scaled 36pt fixed font */
449 fs
= XLoadQueryFont(display
,
450 "-misc-fixed-medium-r-normal--36-*-75-75-c-*-iso8859-*");
453 /* they don't have it! */
454 /* try our next preferred font (100dpi sony) */
455 fprintf(stderr
, "Trying alternate font\n");
456 fs
= XLoadQueryFont(display
,
457 "-sony-fixed-medium-r-normal--24-*-100-100-c-*-iso8859-*");
459 /* they don't have the sony font either */
461 fprintf(stderr
, "Trying \"fixed\" font\n");
462 fs
= XLoadQueryFont(display
,
464 /* if they don't have the fixed font, we've got different kind
467 fprintf(stderr
, "Your X server is probably broken\n");
474 XFreeGC(display
, dockapp
.osd_gc
);
476 XCreateGC(display
, osd
,
477 GCForeground
| GCBackground
| GCGraphicsExposures
,
479 XSetFont(display
, gc
, fs
->fid
);
483 dockapp
.osd_width
= width
;
486 dockapp
.osd_mapped
= false;
489 void update_osd(float volume
, bool up
)
496 foo
= (dockapp
.osd_width
- 20) * volume
/ 20.0;
499 for (i
= 1; i
<= foo
; i
++)
500 XFillRectangle(display
, dockapp
.osd
, dockapp
.osd_gc
,
502 } else if (foo
< bar
) {
503 XClearArea(display
, dockapp
.osd
, ((foo
+1) * 20), 30,
504 ((bar
-foo
) * 20), 25, 1);
505 } else if (foo
> bar
) {
506 for (i
= (bar
> 0 ? bar
: 1); i
<= foo
; i
++)
507 XFillRectangle(display
, dockapp
.osd
, dockapp
.osd_gc
,
517 XUnmapWindow(display
, dockapp
.osd
);
519 dockapp
.osd_mapped
= false;
526 XMapRaised(display
, dockapp
.osd
);
527 XDrawString(display
, dockapp
.osd
, dockapp
.osd_gc
, 1, 25,
528 mixer_get_channel_name(), strlen(mixer_get_channel_name()));
529 update_osd(mixer_get_volume(), true);
531 dockapp
.osd_mapped
= true;
535 bool osd_mapped(void)
537 return dockapp
.osd_mapped
;
540 void set_cursor(int type
)
549 XDefineCursor(display
, win
, null_cursor
);
550 XDefineCursor(display
, iconwin
, null_cursor
);
553 XDefineCursor(display
, win
, norm_cursor
);
554 XDefineCursor(display
, iconwin
, norm_cursor
);
557 XDefineCursor(display
, win
, hand_cursor
);
558 XDefineCursor(display
, iconwin
, hand_cursor
);
561 XDefineCursor(display
, win
, bar_cursor
);
562 XDefineCursor(display
, iconwin
, bar_cursor
);
569 static void draw_stereo_led(void)
571 if (mixer_is_stereo()) /* stereo capable */
572 copy_xpm_area(78, 0, 9, 7, 28, 14); /* light up LCD */
573 else /* mono channel */
574 copy_xpm_area(78, 7, 9, 7, 28, 14); /* turn off LCD */
577 static void draw_rec_led(void)
579 if (mixer_is_rec()) /* record enabled */
580 copy_xpm_area(65, 0, 13, 7, 4, 14); /* Light up LCD */
581 else /* record disabled */
582 copy_xpm_area(65, 7, 13, 7, 4, 14); /* turn off LCD */
585 static void draw_mute_led(void)
587 if (mixer_is_muted()) /* mute */
588 copy_xpm_area(65, 14, 20, 7, 39, 14); /* light up LCD */
590 copy_xpm_area(65, 21, 20, 7, 39, 14); /* turn off LCD */
593 static void draw_percent(float volume
)
595 int vol
= (int)(volume
*100 + 0.5);
597 copy_xpm_area(0, 87, 18, 9, 41, 22); /* clear percentage */
601 copy_xpm_area((vol
/ 10) * 6, 67, 6, 9, 47, 22);
602 copy_xpm_area((vol
% 10) * 6, 67, 6, 9, 53, 22);
604 copy_xpm_area(6, 67, 6, 9, 41, 22);
605 copy_xpm_area(0, 67, 6, 9, 47, 22);
606 copy_xpm_area(0, 67, 6, 9, 53, 22);
610 static void draw_knob(float volume
)
612 float bearing
, led_x
, led_y
;
613 int led_topleft_x
, led_topleft_y
;
616 bearing
= (1.25 * PI
) - (1.5 * PI
) * volume
;
618 led_x
= KNOB_CENTER_X
+ LED_POS_RADIUS
* cos(bearing
);
619 led_y
= KNOB_CENTER_Y
- LED_POS_RADIUS
* sin(bearing
);
621 led_topleft_x
= (int)(led_x
- (LED_WIDTH
/ 2.0) + 0.5);
622 led_topleft_y
= (int)(led_y
- (LED_HEIGHT
/ 2.0) + 0.5);
624 /* clear previous knob picture */
625 copy_xpm_area(87, 0, 26, 26, 36, 35);
627 if (mixer_is_muted())
628 led_pixmap
= led_off_pixmap
;
630 led_pixmap
= led_on_pixmap
;
632 XCopyArea(display
, led_pixmap
, dockapp
.pixmap
, dockapp
.gc
,
633 0, 0, LED_WIDTH
, LED_HEIGHT
, led_topleft_x
, led_topleft_y
);
634 draw_percent(volume
);
637 static void draw_slider(float offset
)
639 int x
= (offset
* 50) / 5;
641 copy_xpm_area(65, 45, 27, 20, 4, 40); /* repair region. move */
642 copy_xpm_area(65, 29, 7, 15, 14 + x
, 43); /* slider */
645 static Cursor
create_null_cursor(Display
*x_display
)
653 cursor_mask
= XCreatePixmap(x_display
, DefaultRootWindow(x_display
), 1, 1, 1);
654 gcval
.function
= GXclear
;
655 gc
= XCreateGC(x_display
, cursor_mask
, GCFunction
, &gcval
);
656 XFillRectangle(x_display
, cursor_mask
, gc
, 0, 0, 1, 1);
657 dummy_color
.pixel
= 0;
659 dummy_color
.flags
= 04;
660 cursor
= XCreatePixmapCursor(x_display
, cursor_mask
, cursor_mask
,
661 &dummy_color
, &dummy_color
, 0, 0);
662 XFreePixmap(x_display
, cursor_mask
);
663 XFreeGC(x_display
, gc
);
668 unsigned long get_color(Display
*display
, char *color_name
)
671 XWindowAttributes winattr
;
674 XGetWindowAttributes(display
,
675 RootWindow(display
, DefaultScreen(display
)), &winattr
);
677 status
= XParseColor(display
, winattr
.colormap
, color_name
, &color
);
679 fprintf(stderr
, "wmix:warning: Could not get color \"%s\" for OSD, falling back to default\n", color_name
);
681 if (color_name
!= default_osd_color
)
682 status
= XParseColor(display
, winattr
.colormap
, default_osd_color
, &color
);
684 return WhitePixel(display
, DefaultScreen(display
));
687 color
.flags
= DoRed
| DoGreen
| DoBlue
;
688 XAllocColor(display
, winattr
.colormap
, &color
);