1 /* wmusic - a xmms remote-controlling DockApp
2 * Copyright (C) 2000-2001 Bastien Nocera <hadess@hadess.net>
3 * Maintained by John Chapin <john+wmusic@jtan.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #define DBL_CLICK_INTERVAL 250 /* double click interval in milliseconds */
21 #define ARROW_INTERVAL 100 /* arrow update interval in milliseconds */
22 #define SCROLL_INTERVAL 300 /* scroll update interval in milliseconds */
23 #define SEPARATOR " ** " /* The separator for the scrolling title */
24 #define DISPLAYSIZE 6 /* width of text to display (running title) */
31 #include <xmms/xmmsctrl.h>
34 #include "wmusic-master.xpm"
35 #include "wmusic-digits.xpm"
37 /*---------------------------------------------------------------------------*/
39 /*---------------------------------------------------------------------------*/
41 void copyNumArea(int x
, int y
, int sx
, int sy
, int dx
, int dy
);
42 void ActionPlay(int x
, int y
, DARect rect
, void *data
);
43 void ActionPause(int x
, int y
, DARect rect
, void *data
);
44 void ActionEject(int x
, int y
, DARect rect
, void *data
);
45 void ActionPrev(int x
, int y
, DARect rect
, void *data
);
46 void ActionNext(int x
, int y
, DARect rect
, void *data
);
47 void ActionStop(int x
, int y
, DARect rect
, void *data
);
48 void ActionFastr(int x
, int y
, DARect rect
, void *data
);
49 void ActionFastf(int x
, int y
, DARect rect
, void *data
);
50 void ToggleWins(int x
, int y
, DARect rect
, void *data
);
51 void ToggleVol(int x
, int y
, DARect rect
, void *data
);
52 void ChangeVol(int x
, int y
, DARect rect
, void *data
);
53 void ToggleTitle(int x
, int y
, DARect rect
, void *data
);
54 void ToggleTime(int x
, int y
, DARect rect
, void *data
);
55 void buttonPress(int button
, int state
, int x
, int y
);
56 void buttonRelease(int button
, int state
, int x
, int y
);
57 void DisplayRoutine();
58 void DrawPos (int pos
);
59 void DrawTime(int time
);
61 void DrawChannels(int channels
);
62 void DrawKbps(int bps
);
63 void DrawTitle(char *title
);
64 void ExecuteXmms(void);
66 /*----------------------------------------------------------------------------*/
68 /*----------------------------------------------------------------------------*/
71 char *displayName
= "";
75 /* Dockapp variables */
76 unsigned int xmms_session
= 0;
77 char *xmms_cmd
= "xmms";
78 Bool main_vis
=0, pl_vis
=0, eq_vis
=0;
80 unsigned int volume_step
= 5;
87 unsigned int arrow_pos
= 0;
88 Bool pause_norotate
= 0;
91 Pixmap pixmap
, mask
; /* Pixmap that is displayed */
92 Pixmap pixnum
, masknum
; /* Pixmap source */
94 int left_pressed
= 0; /* for pseudo drag callback */
95 int motion_event
= 0; /* on motion events we do not want(too fast) display update */
97 static DAActionRect buttonRects
[] = {
98 {{5, 39, 14, 9}, ActionPrev
},
99 {{19, 39, 14, 9}, ActionNext
},
100 {{33, 39, 13, 9}, ActionFastr
},
101 {{46, 39, 13, 9}, ActionFastf
},
102 {{5, 48, 11, 11}, ActionEject
},
103 {{16, 48, 21, 11}, ActionPlay
},
104 {{37, 48, 11, 11}, ActionPause
},
105 {{48, 48, 11, 11}, ActionStop
}
108 static DAActionRect toggleRect
[] = {
109 {{5, 5, 54, 30}, ToggleWins
}
112 static DAActionRect globRect
[] = {
113 {{0, 0, 64, 64}, ToggleVol
}
116 static DAActionRect displayRects
[] = {
117 {{5, 5, 54, 12}, ToggleTime
},
118 {{5, 25, 54, 9}, ToggleTitle
}
121 static DAActionRect volumeRects
[] = {
122 {{5, 17, 38, 8 }, ChangeVol
}
125 static DAProgramOption options
[] = {
126 {"-c", "--command", "Command to launch xmms", DOString
, False
,
128 {"-d", "--display", "Display to use", DOString
, False
, {&displayName
} },
129 {"-r", "--run", "Run xmms on startup", DONone
, False
, {NULL
} },
130 {"-s", "--session", "Xmms session to use", DONatural
, False
,
132 {"-t", "--title", "Show title instead of kbps by default", DONone
,
134 {"-u", "--hide", "Hide xmms windows on startup", DONone
, False
,
136 {"-V", "--volume", "Stepping of the wheel volume control (in percent)",
137 DONatural
, False
, {&volume_step
} },
138 {"-x", "--wmxmms", "Use wmxmms-like show and hide", DONone
, False
,
140 {"-a", "--rotate-arrow", "Do not rotate the arrow, when paused",
141 DONone
, False
, {NULL
} },
142 {"-l", "--time-left", "Show time left instead of time remaining by default",
143 DONone
, False
, {NULL
} },
144 {"-b", "--volume-slider", "Show volume slider instead of channels",
145 DONone
, False
, {NULL
}},
146 {"-R", "--run-excusive", "Run xmms on startup, exit when xmms exits", DONone
, False
, {NULL
} }
156 static glyphdescr glyphs
[] = {
157 {L
'-', 67, 83}, {L
'.', 73, 83}, {L
'\x27', 79, 83},
158 {L
'(', 85, 83}, {L
')', 91, 83}, {L
'*', 97, 83}, {L
'/', 103, 83},
160 {L
'0', 1, 83}, {L
'1', 7, 83}, {L
'2', 13, 83}, {L
'3', 19, 83}, {L
'4', 25, 83},
161 {L
'5', 31, 83}, {L
'6', 37, 83}, {L
'7', 43, 83}, {L
'8', 49, 83}, {L
'9', 55, 83},
165 {L
'A', 1, 73}, {L
'a', 1, 73},
166 {L
'B', 7, 73}, {L
'b', 7, 73},
167 {L
'C', 13, 73}, {L
'c', 13, 73},
168 {L
'D', 19, 73}, {L
'd', 19, 73},
169 {L
'E', 25, 73}, {L
'e', 25, 73},
171 {L
'F', 31, 73}, {L
'f', 31, 73},
172 {L
'G', 37, 73}, {L
'g', 37, 73},
173 {L
'H', 43, 73}, {L
'h', 43, 73},
174 {L
'I', 49, 73}, {L
'i', 49, 73},
175 {L
'J', 55, 73}, {L
'j', 55, 73},
177 {L
'K', 61, 73}, {L
'k', 61, 73},
178 {L
'L', 67, 73}, {L
'l', 67, 73},
179 {L
'M', 73, 73}, {L
'm', 73, 73},
180 {L
'N', 79, 73}, {L
'n', 79, 73},
181 {L
'O', 85, 73}, {L
'o', 85, 73},
183 {L
'P', 91, 73}, {L
'p', 91, 73},
184 {L
'Q', 97, 73}, {L
'q', 97, 73},
185 {L
'R',103, 73}, {L
'r',103, 73},
186 {L
'S',109, 73}, {L
's',109, 73},
187 {L
'T',115, 73}, {L
't',115, 73},
189 {L
'U',121, 73}, {L
'u',121, 73},
190 {L
'V',127, 73}, {L
'v',127, 73},
191 {L
'W',133, 73}, {L
'w',133, 73},
192 {L
'X',139, 73}, {L
'x',139, 73},
193 {L
'Y',145, 73}, {L
'y',145, 73},
195 {L
'Z',151, 73}, {L
'z',151, 73},
198 {L
'\x42e', 1, 93}, {L
'\x44e', 1, 93}, /* cyrillic Yu */
200 {L
'\x410', 7, 93}, {L
'\x430', 7, 93}, /* cyrillic A */
201 {L
'\x411', 13, 93}, {L
'\x431', 13, 93}, /* cyrillic Be */
202 {L
'\x426', 19, 93}, {L
'\x446', 19, 93}, /* cyrillic Ce */
203 {L
'\x414', 25, 93}, {L
'\x434', 25, 93}, /* cyrillic De */
204 {L
'\x415', 31, 93}, {L
'\x435', 31, 93}, /* cyrillic Ye */
206 {L
'\x424', 37, 93}, {L
'\x444', 37, 93}, /* cyrillic eF */
207 {L
'\x413', 43, 93}, {L
'\x433', 43, 93}, /* cyrillic Ge */
208 {L
'\x425', 49, 93}, {L
'\x445', 49, 93}, /* cyrillic Ha */
209 {L
'\x418', 55, 93}, {L
'\x438', 55, 93}, /* cyrillic I */
210 {L
'\x419', 61, 93}, {L
'\x439', 61, 93}, /* cyrillic I-kratkoe */
212 {L
'\x41a', 67, 93}, {L
'\x43a', 67, 93}, /* cyrillic Ka */
213 {L
'\x41b', 73, 93}, {L
'\x43b', 73, 93}, /* cyrillic eL */
214 {L
'\x41c', 79, 93}, {L
'\x43c', 79, 93}, /* cyrillic eM */
215 {L
'\x41d', 85, 93}, {L
'\x43d', 85, 93}, /* cyrillic eN */
216 {L
'\x41e', 91, 93}, {L
'\x43e', 91, 93}, /* cyrillic O */
218 {L
'\x41f', 97, 93}, {L
'\x43f', 97, 93}, /* cyrillic Pe */
219 {L
'\x42f',103, 93}, {L
'\x44f',103, 93}, /* cyrillic Ya */
220 {L
'\x420',109, 93}, {L
'\x440',109, 93}, /* cyrillic eR */
221 {L
'\x421',115, 93}, {L
'\x441',115, 93}, /* cyrillic eS */
222 {L
'\x422',121, 93}, {L
'\x442',121, 93}, /* cyrillic Te */
224 {L
'\x423',127, 93}, {L
'\x443',127, 93}, /* cyrillic U */
225 {L
'\x416',133, 93}, {L
'\x436',133, 93}, /* cyrillic Je */
226 {L
'\x412',139, 93}, {L
'\x432',139, 93}, /* cyrillic Ve */
227 {L
'\x42c',145, 93}, {L
'\x44c',145, 93}, /* cyrillic MyagkijZnak */
228 {L
'\x42b',151, 93}, {L
'\x44b',151, 93}, /* cyrillic Y */
230 {L
'\x417',157, 93}, {L
'\x437',157, 93}, /* cyrillic Ze */
231 {L
'\x428',163, 93}, {L
'\x448',163, 93}, /* cyrillic Sha */
232 {L
'\x42d',169, 93}, {L
'\x44d',169, 93}, /* cyrillic E */
233 {L
'\x429',175, 93}, {L
'\x449',175, 93}, /* cyrillic Scha */
234 {L
'\x427',181, 93}, {L
'\x447',181, 93}, /* cyrillic Che */
236 {L
'\x42a',187, 93}, {L
'\x44a',187, 93}, /* cyrillic TvyordyiZnak */
237 {L
'\x404',115, 83}, {L
'\x454',115, 83}, /* ukrainian IE */
238 {L
'\x406', 49, 73}, {L
'\x456', 49, 73}, /* ukrainian I */
239 {L
'\x407',109, 83}, {L
'\x457',109, 83}, /* ukrainian YI */
240 {L
'\x491', 43, 93}, {L
'\x490', 43, 93}, /* ukrainian GHE with upturn */
242 {L
'\x401',121, 83}, {L
'\x451',121, 83}, /* cyrillic Yo */
247 /*----------------------------------------------------------------------------*/
249 /*----------------------------------------------------------------------------*/
251 void copyNumArea(int x
, int y
, int sx
, int sy
, int dx
, int dy
)
253 XCopyArea(DADisplay
, pixnum
, pixmap
, gc
, x
, y
, sx
, sy
, dx
, dy
);
256 void buttonDraw(DARect rect
)
258 copyNumArea((rect
.x
)-5, (rect
.y
)-8, rect
.width
, rect
.height
,
263 void ActionPlay(int x
, int y
, DARect rect
, void *data
)
268 xmms_remote_play(xmms_session
);
272 void ActionPause(int x
, int y
, DARect rect
, void *data
)
277 xmms_remote_pause(xmms_session
);
281 void ActionEject(int x
, int y
, DARect rect
, void *data
)
286 xmms_remote_eject(xmms_session
);
290 void ActionPrev(int x
, int y
, DARect rect
, void *data
)
295 xmms_remote_playlist_prev(xmms_session
);
299 void ActionNext(int x
, int y
, DARect rect
, void *data
)
304 xmms_remote_playlist_next(xmms_session
);
308 void ActionStop(int x
, int y
, DARect rect
, void *data
)
313 xmms_remote_stop(xmms_session
);
317 void ActionFastr(int x
, int y
, DARect rect
, void *data
)
322 xmms_remote_jump_to_time(xmms_session
,
323 xmms_remote_get_output_time(xmms_session
)-10000);
327 void ActionFastf(int x
, int y
, DARect rect
, void *data
)
332 xmms_remote_jump_to_time(xmms_session
,
333 xmms_remote_get_output_time(xmms_session
)+10000);
337 void ToggleWins(int x
, int y
, DARect rect
, void *data
)
344 xmms_remote_main_win_toggle(xmms_session
,
345 !xmms_remote_is_main_win(xmms_session
));
347 xmms_remote_pl_win_toggle(xmms_session
,
348 !xmms_remote_is_pl_win(xmms_session
));
350 if (xmms_remote_is_main_win(xmms_session
) ||
351 xmms_remote_is_pl_win(xmms_session
) ||
352 xmms_remote_is_eq_win(xmms_session
))
354 main_vis
= xmms_remote_is_main_win(xmms_session
);
355 pl_vis
= xmms_remote_is_pl_win(xmms_session
);
356 eq_vis
= xmms_remote_is_eq_win(xmms_session
);
358 xmms_remote_main_win_toggle(xmms_session
, 0);
359 xmms_remote_pl_win_toggle(xmms_session
, 0);
360 xmms_remote_eq_win_toggle(xmms_session
, 0);
362 xmms_remote_main_win_toggle(xmms_session
, main_vis
);
363 xmms_remote_pl_win_toggle(xmms_session
, pl_vis
);
364 xmms_remote_eq_win_toggle(xmms_session
, eq_vis
);
368 if ( (ev
.xbutton
.time
-click_time
) <= DBL_CLICK_INTERVAL
)
373 click_time
=ev
.xbutton
.time
;
378 void ToggleVol(int x
, int y
, DARect rect
, void *data
)
383 if (*(int*)data
== 1)
384 factor
= volume_step
;
386 factor
= -volume_step
;
388 /* xmms seems to do the bounds checking for us */
390 volume
= xmms_remote_get_main_volume(xmms_session
);
391 xmms_remote_set_main_volume(xmms_session
, volume
+factor
);
394 void ChangeVol(int x
, int y
, DARect rect
, void *data
)
396 int volume
= (int)100*(((float)x
)/38);
397 xmms_remote_set_main_volume(xmms_session
, volume
);
400 void ToggleTitle(int x
, int y
, DARect rect
, void *data
)
407 void ToggleTime(int x
, int y
, DARect rect
, void *data
)
414 void buttonPress(int button
, int state
, int x
, int y
)
423 DAProcessActionRects(x
, y
, buttonRects
, sizeof(buttonRects
)/sizeof(DAActionRect
), tmp
);
424 DAProcessActionRects(x
, y
, displayRects
, sizeof(displayRects
)/sizeof(DAActionRect
), tmp
);
428 DAProcessActionRects(x
, y
, toggleRect
, sizeof(toggleRect
)/sizeof(DAActionRect
), NULL
);
433 DAProcessActionRects(x
, y
, toggleRect
, sizeof(toggleRect
)/sizeof(DAActionRect
), tmp
);
435 if ((button
== 4) || (button
== 5))
439 /* Wheel scrolls down */
441 DAProcessActionRects(x
, y
, globRect
, sizeof(globRect
)/sizeof(DAActionRect
), &tmp
);
443 /* Wheel scrolls up */
445 DAProcessActionRects(x
, y
, globRect
, sizeof(globRect
)/sizeof(DAActionRect
), &tmp
);
450 DAProcessActionRects(x
, y
, toggleRect
, sizeof(toggleRect
)/sizeof(DAActionRect
), NULL
);
453 void buttonRelease(int button
, int state
, int x
, int y
)
461 copyNumArea(0,51, 54, 20, 5,39);
463 DAProcessActionRects(x
, y
, buttonRects
, sizeof(buttonRects
)/sizeof(DAActionRect
), NULL
);
468 void buttonDrag(int x
, int y
)
472 if (left_pressed
==1) {
473 DAProcessActionRects(x
,y
, volumeRects
, 1, NULL
);
474 DrawChannels(0); /* volume only update */
479 void DisplayRoutine()
481 int time
= 0, length
= 0, position
= 100;
484 Bool do_scroll
=t_scroll
;
486 xmms_running
=xmms_remote_is_running(xmms_session
);
493 position
= 100; /* Will display "00" as the current position */
494 do_scroll
= 0; /* Will display channel/kbps info */
502 /* check again, avoiding xmms_remote_* functions to hang,
503 if xmms was closed in the meantime */
504 if ((xmms_running
= xmms_remote_is_running(xmms_session
)))
506 position
= xmms_remote_get_playlist_pos(xmms_session
);
507 xmms_remote_get_info(xmms_session
, &rate
, &freq
, &nch
);
508 time
= xmms_remote_get_output_time(xmms_session
);
509 length
= xmms_remote_get_playlist_time(xmms_session
, position
);
510 title
= xmms_remote_get_playlist_title(xmms_session
, position
);
511 if (!xmms_remote_is_playing(xmms_session
) || (pause_norotate
&&
512 xmms_remote_is_paused(xmms_session
) ))
518 if (t_time
) DrawTime(length
-time
);
523 if (do_scroll
&& (title
!= NULL
))
534 void DrawPos (int pos
)
542 sprintf(posstr
, "%02d", pos
);
546 copyNumArea((*p
-'0')*6 + 1, 1, 6, 7, (i
*6)+39, 7);
551 void DrawTime(int time
)
560 * up to 99 minutes and 59 seconds
561 * up to 99 hours and 59 minutes
566 sprintf(timestr
, "%02d%02d", time
/ 60, time
% 60);
570 sprintf(timestr
, "%02d%02d", time
/ 3600,
573 sprintf(timestr
, "%02d%02d", 0, 0);
579 copyNumArea((*p
-'0')*7 + 2, 11, 7, 9, i
<2 ?(i
*7)+7:(i
*7)+12, 7);
586 copyNumArea((arrow_pos
*8)+30, 22, 8, 9, 47, 19);
588 if (arrow_pos
> 4) arrow_pos
= 0;
591 void DrawChannels(int channels
)
595 int volume
= (int)(0.36*(float)xmms_remote_get_main_volume(xmms_session
));
598 copyNumArea(61, 0, volume
, 6, 7, 18);
599 copyNumArea(97, 0, 36-volume
, 6, 7+volume
, 18);
603 copyNumArea(55, 32, 10, 6, 10, 18);
605 copyNumArea((channels
*10), 22, 10, 6, 10, 18);
610 void DrawKbps(int bps
)
616 if (bps
> 999000) bps
=0;
617 sprintf(kbpstr
, "%03d", bps
/ 1000);
621 copyNumArea((*p
-'0')*6 + 1, 1, 6, 7, (i
*6)+1, 26);
624 copyNumArea(55, 39, 18, 8, 25, 26);
627 void DrawChar(wchar_t wc
, int x
, int y
)
630 for(i
= 0; i
< sizeof(glyphs
)/sizeof(glyphdescr
) - 1; ++i
)
632 if(wc
== glyphs
[i
].c
)
635 copyNumArea(glyphs
[i
].x
, glyphs
[i
].y
, 6, 8, x
, y
);
638 int DrawChars(char *title
, int tpos
, int pos
)
642 mbtowc(NULL
, NULL
, 0);
643 while(*title
&& (pos
<= (tpos
+ DISPLAYSIZE
)))
645 int len
= mbtowc(&wc
, title
, MB_CUR_MAX
);
648 DrawChar(wc
, (pos
- tpos
)*6 + 1, 26);
654 void DrawTitle(char *name
)
656 int len
, pos
, tpos
= title_pos
;
661 printf("name: %s\n", name
);
663 len
= pos
= DrawChars(name
, tpos
, 0);
666 DrawChars(" ", tpos
, pos
);
670 if(pos
<= tpos
+ DISPLAYSIZE
)
671 pos
= DrawChars(SEPARATOR
, tpos
, pos
);
673 if(pos
<= tpos
+ DISPLAYSIZE
)
674 DrawChars(name
, tpos
, pos
);
676 if(tpos
>= len
+ strlen(SEPARATOR
))
679 title_pos
= title_pos
+ 0.5;
682 void ExecuteXmms(void)
687 command
=malloc(strlen(xmms_cmd
)+5);
688 sprintf(command
, "%s &", xmms_cmd
);
689 status
= system(command
);
692 fprintf(stderr
, "XMMS can't be launched, exiting...");
695 while (!xmms_remote_is_running(xmms_session
))
699 /*----------------------------------------------------------------------------*/
701 /*----------------------------------------------------------------------------*/
703 int main(int argc
, char **argv
)
705 unsigned height
, width
;
706 DACallbacks callbacks
={NULL
, buttonPress
, buttonRelease
, buttonDrag
,
710 DAParseArguments(argc
, argv
, options
,
711 sizeof(options
)/sizeof(DAProgramOption
),
712 "XMMS remote control by Bastien Nocera <hadess@hadess.net>",
715 setlocale(LC_ALL
, "");
716 DAInitialize(displayName
, "wmusic", 64, 64, argc
, argv
);
717 DASetCallbacks(&callbacks
);
720 DAMakePixmapFromData(wmusic_master_xpm
, &pixmap
,
721 &mask
, &height
, &width
);
722 DAMakePixmapFromData(wmusic_digits_xpm
, &pixnum
,
723 &masknum
, &height
, &width
);
724 gc
= DefaultGC(DADisplay
, DefaultScreen(DADisplay
));
725 DASetShapeWithOffset(mask
, 0, 0);
730 /* End of initialization */
732 if (options
[4].used
) t_scroll
=1;
733 if (options
[8].used
) pause_norotate
=1;
734 if (options
[9].used
) t_time
=1;
735 if (options
[10].used
) t_volume
=1;
736 if (options
[11].used
) run_excusive
=1;
738 /* Launch xmms if it's not running and -r or -R was used */
739 xmms_running
=xmms_remote_is_running(xmms_session
);
740 if ((!xmms_running
) && (options
[2].used
|| run_excusive
))
746 if(xmms_remote_is_main_win(xmms_session
) && (options
[6].used
))
747 ToggleWins(0, 0, DANoRect
, NULL
);
749 /* Update the display */
753 if (DANextEventOrTimeout(&ev
, 100))