wmclockmon: update change-log
[dockapps.git] / wmusic / src / wmusic.c
blob7bdba404546737797689e00d55e136bbc1db150e
1 /* wmusic - an MPRIS-compatible media player remote-controlling DockApp
2 * Copyright (C) 2000-2001 Bastien Nocera <hadess@hadess.net>
3 * Copyright (C) 2002-2004 John Chapin <john+wmusic@jtan.com>
4 * Copyright (C) 2018-2023 Window Maker Team <wmaker-dev@googlegroups.com>
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #define DBL_CLICK_INTERVAL 250 /* double click interval in milliseconds */
22 #define ARROW_INTERVAL 100 /* arrow update interval in milliseconds */
23 #define SCROLL_INTERVAL 300 /* scroll update interval in milliseconds */
24 #define SEPARATOR " ** " /* The separator for the scrolling title */
25 #define DISPLAYSIZE 6 /* width of text to display (running title) */
27 #define PLAYER_INSTANCE_FROM_LIST(x) ((PlayerctlPlayerName *)x->data)->instance
29 #include <libdockapp/dockapp.h>
30 #include <playerctl/playerctl.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <locale.h>
36 #include <X11/X.h>
37 #include <X11/Xlib.h>
39 #include "wmusic-master.xpm"
40 #include "wmusic-digits.xpm"
42 /*---------------------------------------------------------------------------*/
43 /* Prototypes */
44 /*---------------------------------------------------------------------------*/
46 void copyNumArea(int x, int y, int sx, int sy, int dx, int dy);
47 void ActionPlay(int x, int y, DARect rect, void *data);
48 void ActionPause(int x, int y, DARect rect, void *data);
49 void ActionEject(int x, int y, DARect rect, void *data);
50 void ActionPrev(int x, int y, DARect rect, void *data);
51 void ActionNext(int x, int y, DARect rect, void *data);
52 void ActionStop(int x, int y, DARect rect, void *data);
53 void ActionFastr(int x, int y, DARect rect, void *data);
54 void ActionFastf(int x, int y, DARect rect, void *data);
55 void ToggleWins(int x, int y, DARect rect, void *data);
56 void ToggleVol(int x, int y, DARect rect, void *data);
57 void ChangeVol(int x, int y, DARect rect, void *data);
58 void ToggleTime(int x, int y, DARect rect, void *data);
59 void buttonPress(int button, int state, int x, int y);
60 void buttonRelease(int button, int state, int x, int y);
61 int PlayerConnect(void);
62 void DisplayRoutine();
63 void DrawTrackNum (int track_num);
64 void DrawTime(gint64 time);
65 void DrawArrow(void);
66 void DrawVolume(void);
67 void DrawTitle(char *title);
68 void ExecuteXmms(void);
70 /*----------------------------------------------------------------------------*/
71 /* Variables */
72 /*----------------------------------------------------------------------------*/
74 /* X11 variables */
75 char *displayName = "";
76 GC gc;
77 XEvent ev;
79 /* Dockapp variables */
80 PlayerctlPlayer *player;
81 char *xmms_cmd = "xmms";
82 Bool main_vis=0, pl_vis=0, eq_vis=0;
83 unsigned int volume_step = 5;
84 Bool run_excusive=0;
86 Bool t_time=0;
87 float title_pos = 0;
88 unsigned int arrow_pos = 0;
89 Bool pause_norotate = 0;
90 Time click_time=0;
92 Pixmap pixmap, mask; /* Pixmap that is displayed */
93 Pixmap pixnum, masknum; /* Pixmap source */
95 int left_pressed = 0; /* for pseudo drag callback */
96 int motion_event = 0; /* on motion events we do not want(too fast) display update */
98 static DAActionRect buttonRects[] = {
99 {{5, 39, 14, 9}, ActionPrev},
100 {{19, 39, 14, 9}, ActionNext},
101 {{33, 39, 13, 9}, ActionFastr},
102 {{46, 39, 13, 9}, ActionFastf},
103 {{5, 48, 11, 11}, ActionEject},
104 {{16, 48, 21, 11}, ActionPlay},
105 {{37, 48, 11, 11}, ActionPause},
106 {{48, 48, 11, 11}, ActionStop}
109 static DAActionRect toggleRect[] = {
110 {{5, 5, 54, 30}, ToggleWins}
113 static DAActionRect globRect[] = {
114 {{0, 0, 64, 64}, ToggleVol}
117 static DAActionRect displayRects[] = {
118 {{5, 5, 54, 12}, ToggleTime},
121 static DAActionRect volumeRects[] = {
122 {{5, 17, 38, 8 }, ChangeVol}
125 static DAProgramOption options[] = {
126 {"-c", "--command", "Command to launch the media player", DOString,
127 False, {&xmms_cmd} },
128 {"-d", "--display", "Display to use", DOString, False, {&displayName} },
129 {"-r", "--run", "Run the media player on startup", DONone, False,
130 {NULL} },
131 {"-V", "--volume", "Stepping of the wheel volume control (in percent)",
132 DONatural, False, {&volume_step} },
133 {"-a", "--rotate-arrow", "Do not rotate the arrow, when paused",
134 DONone, False, {NULL} },
135 {"-l", "--time-left", "Show time left instead of time remaining by default",
136 DONone, False, {NULL} },
137 {"-R", "--run-excusive", "Run media player on startup, "
138 "exit when it exits", DONone, False, {NULL} }
141 typedef struct
143 wchar_t c;
144 int x;
145 int y;
146 } glyphdescr;
148 static glyphdescr glyphs[] = {
149 {L'-', 67, 83}, {L'.', 73, 83}, {L'\x27', 79, 83},
150 {L'(', 85, 83}, {L')', 91, 83}, {L'*', 97, 83}, {L'/', 103, 83},
152 {L'0', 1, 83}, {L'1', 7, 83}, {L'2', 13, 83}, {L'3', 19, 83}, {L'4', 25, 83},
153 {L'5', 31, 83}, {L'6', 37, 83}, {L'7', 43, 83}, {L'8', 49, 83}, {L'9', 55, 83},
157 {L'A', 1, 73}, {L'a', 1, 73},
158 {L'B', 7, 73}, {L'b', 7, 73},
159 {L'C', 13, 73}, {L'c', 13, 73},
160 {L'D', 19, 73}, {L'd', 19, 73},
161 {L'E', 25, 73}, {L'e', 25, 73},
163 {L'F', 31, 73}, {L'f', 31, 73},
164 {L'G', 37, 73}, {L'g', 37, 73},
165 {L'H', 43, 73}, {L'h', 43, 73},
166 {L'I', 49, 73}, {L'i', 49, 73},
167 {L'J', 55, 73}, {L'j', 55, 73},
169 {L'K', 61, 73}, {L'k', 61, 73},
170 {L'L', 67, 73}, {L'l', 67, 73},
171 {L'M', 73, 73}, {L'm', 73, 73},
172 {L'N', 79, 73}, {L'n', 79, 73},
173 {L'O', 85, 73}, {L'o', 85, 73},
175 {L'P', 91, 73}, {L'p', 91, 73},
176 {L'Q', 97, 73}, {L'q', 97, 73},
177 {L'R',103, 73}, {L'r',103, 73},
178 {L'S',109, 73}, {L's',109, 73},
179 {L'T',115, 73}, {L't',115, 73},
181 {L'U',121, 73}, {L'u',121, 73},
182 {L'V',127, 73}, {L'v',127, 73},
183 {L'W',133, 73}, {L'w',133, 73},
184 {L'X',139, 73}, {L'x',139, 73},
185 {L'Y',145, 73}, {L'y',145, 73},
187 {L'Z',151, 73}, {L'z',151, 73},
190 {L'\x42e', 1, 93}, {L'\x44e', 1, 93}, /* cyrillic Yu */
192 {L'\x410', 7, 93}, {L'\x430', 7, 93}, /* cyrillic A */
193 {L'\x411', 13, 93}, {L'\x431', 13, 93}, /* cyrillic Be */
194 {L'\x426', 19, 93}, {L'\x446', 19, 93}, /* cyrillic Ce */
195 {L'\x414', 25, 93}, {L'\x434', 25, 93}, /* cyrillic De */
196 {L'\x415', 31, 93}, {L'\x435', 31, 93}, /* cyrillic Ye */
198 {L'\x424', 37, 93}, {L'\x444', 37, 93}, /* cyrillic eF */
199 {L'\x413', 43, 93}, {L'\x433', 43, 93}, /* cyrillic Ge */
200 {L'\x425', 49, 93}, {L'\x445', 49, 93}, /* cyrillic Ha */
201 {L'\x418', 55, 93}, {L'\x438', 55, 93}, /* cyrillic I */
202 {L'\x419', 61, 93}, {L'\x439', 61, 93}, /* cyrillic I-kratkoe */
204 {L'\x41a', 67, 93}, {L'\x43a', 67, 93}, /* cyrillic Ka */
205 {L'\x41b', 73, 93}, {L'\x43b', 73, 93}, /* cyrillic eL */
206 {L'\x41c', 79, 93}, {L'\x43c', 79, 93}, /* cyrillic eM */
207 {L'\x41d', 85, 93}, {L'\x43d', 85, 93}, /* cyrillic eN */
208 {L'\x41e', 91, 93}, {L'\x43e', 91, 93}, /* cyrillic O */
210 {L'\x41f', 97, 93}, {L'\x43f', 97, 93}, /* cyrillic Pe */
211 {L'\x42f',103, 93}, {L'\x44f',103, 93}, /* cyrillic Ya */
212 {L'\x420',109, 93}, {L'\x440',109, 93}, /* cyrillic eR */
213 {L'\x421',115, 93}, {L'\x441',115, 93}, /* cyrillic eS */
214 {L'\x422',121, 93}, {L'\x442',121, 93}, /* cyrillic Te */
216 {L'\x423',127, 93}, {L'\x443',127, 93}, /* cyrillic U */
217 {L'\x416',133, 93}, {L'\x436',133, 93}, /* cyrillic Je */
218 {L'\x412',139, 93}, {L'\x432',139, 93}, /* cyrillic Ve */
219 {L'\x42c',145, 93}, {L'\x44c',145, 93}, /* cyrillic MyagkijZnak */
220 {L'\x42b',151, 93}, {L'\x44b',151, 93}, /* cyrillic Y */
222 {L'\x417',157, 93}, {L'\x437',157, 93}, /* cyrillic Ze */
223 {L'\x428',163, 93}, {L'\x448',163, 93}, /* cyrillic Sha */
224 {L'\x42d',169, 93}, {L'\x44d',169, 93}, /* cyrillic E */
225 {L'\x429',175, 93}, {L'\x449',175, 93}, /* cyrillic Scha */
226 {L'\x427',181, 93}, {L'\x447',181, 93}, /* cyrillic Che */
228 {L'\x42a',187, 93}, {L'\x44a',187, 93}, /* cyrillic TvyordyiZnak */
229 {L'\x404',115, 83}, {L'\x454',115, 83}, /* ukrainian IE */
230 {L'\x406', 49, 73}, {L'\x456', 49, 73}, /* ukrainian I */
231 {L'\x407',109, 83}, {L'\x457',109, 83}, /* ukrainian YI */
232 {L'\x491', 43, 93}, {L'\x490', 43, 93}, /* ukrainian GHE with upturn */
234 {L'\x401',121, 83}, {L'\x451',121, 83}, /* cyrillic Yo */
236 {L' ', 61, 83}
239 /*----------------------------------------------------------------------------*/
240 /* Functions */
241 /*----------------------------------------------------------------------------*/
243 void copyNumArea(int x, int y, int sx, int sy, int dx, int dy)
245 XCopyArea(DADisplay, pixnum, pixmap, gc, x, y, sx, sy, dx, dy);
248 void buttonDraw(DARect rect)
250 copyNumArea((rect.x)-5, (rect.y)-8, rect.width, rect.height,
251 rect.x, rect.y);
252 DASetPixmap(pixmap);
255 void ActionPlay(int x, int y, DARect rect, void *data)
257 if (data) {
258 buttonDraw(rect);
259 } else {
260 GError *error = NULL;
262 playerctl_player_play(player, &error);
263 if (error != NULL)
264 DAWarning("Could not execute command: %s",
265 error->message);
266 g_clear_error(&error);
270 void ActionPause(int x, int y, DARect rect, void *data)
272 if (data) {
273 buttonDraw(rect);
274 } else {
275 GError *error = NULL;
277 playerctl_player_pause(player, &error);
278 if (error != NULL)
279 DAWarning("Could not execute command: %s",
280 error->message);
281 g_clear_error(&error);
285 int CurrentPlayerIndex(GList *players)
287 int i, n;
288 char *player_instance;
290 i = 0;
291 n = g_list_length(players);
292 g_object_get(player, "player-instance", &player_instance, NULL);
294 for (; i < n; i++) {
295 if (g_strcmp0(PLAYER_INSTANCE_FROM_LIST(
296 g_list_nth(players, i)),
297 player_instance) == 0)
298 break;
301 if (i == n)
302 i = -1;
304 g_free(player_instance);
306 return i;
309 void ActionEject(int x, int y, DARect rect, void *data)
311 if (data) {
312 buttonDraw(rect);
313 } else {
314 GList *players;
315 GError *error;
316 int i, n;
318 error = NULL;
319 players = playerctl_list_players(&error);
320 if (error)
321 DAWarning("%s", error->message);
322 g_clear_error(&error);
324 n = g_list_length(players);
325 if (n > 0) {
326 if (!player)
327 i = 0;
328 else {
329 i = CurrentPlayerIndex(players);
331 if (i == -1) /* can't find current player */
332 i = 0;
333 else
334 i = (i + 1) % n;
336 g_object_unref(player);
338 player = playerctl_player_new_from_name(
339 g_list_nth_data(players, i), &error);
340 if (error)
341 DAWarning("Connection to player "
342 "failed: %s", error->message);
343 g_clear_error(&error);
345 DAWarning("Connected to %s",
346 PLAYER_INSTANCE_FROM_LIST(
347 g_list_nth(players, i)));
351 g_list_free_full(players,
352 (GDestroyNotify)playerctl_player_name_free);
356 void ActionPrev(int x, int y, DARect rect, void *data)
358 if (data) {
359 buttonDraw(rect);
360 } else {
361 GError *error = NULL;
363 playerctl_player_previous(player, &error);
364 if (error != NULL)
365 DAWarning("Could not execute command: %s",
366 error->message);
367 g_clear_error(&error);
371 void ActionNext(int x, int y, DARect rect, void *data)
373 if (data) {
374 buttonDraw(rect);
375 } else {
376 GError *error = NULL;
378 playerctl_player_next(player, &error);
379 if (error != NULL)
380 DAWarning("Could not execute command: %s",
381 error->message);
382 g_clear_error(&error);
386 void ActionStop(int x, int y, DARect rect, void *data)
388 if (data) {
389 buttonDraw(rect);
390 } else {
391 GError *error = NULL;
393 playerctl_player_stop(player, &error);
394 if (error != NULL)
395 DAWarning("Could not execute command: %s",
396 error->message);
397 g_clear_error(&error);
401 void ActionFastr(int x, int y, DARect rect, void *data)
403 if (data) {
404 buttonDraw(rect);
405 } else {
406 GError *error = NULL;
408 playerctl_player_seek(player, -10000000, &error);
409 if (error != NULL)
410 DAWarning("Could not execute command: %s",
411 error->message);
412 g_clear_error(&error);
416 void ActionFastf(int x, int y, DARect rect, void *data)
418 if (data) {
419 buttonDraw(rect);
420 } else {
421 GError *error = NULL;
423 playerctl_player_seek(player, 10000000, &error);
424 if (error != NULL)
425 DAWarning("Could not execute command: %s",
426 error->message);
427 g_clear_error(&error);
431 void ToggleWins(int x, int y, DARect rect, void *data)
433 if (!player) {
434 if ( (ev.xbutton.time-click_time) <= DBL_CLICK_INTERVAL )
436 click_time=0;
437 ExecuteXmms();
438 } else {
439 click_time=ev.xbutton.time;
444 void ToggleVol(int x, int y, DARect rect, void *data)
446 double volume;
447 double factor;
448 GError *err;
450 g_object_get(player, "volume", &volume, NULL);
452 if (*(int*)data == 1)
453 factor = 0.01 * volume_step;
454 else
455 factor = -0.01 * volume_step;
456 volume += factor;
458 if (volume > 1)
459 volume = 1;
460 if (volume < 0)
461 volume = 0;
463 err = NULL;
464 playerctl_player_set_volume(player, volume, &err);
465 if (err)
466 DAWarning("Error setting volume: %s", err->message);
467 g_clear_error(&err);
470 void ChangeVol(int x, int y, DARect rect, void *data)
472 float volume = ((float)x)/38;
473 GError *err;
475 err = NULL;
476 playerctl_player_set_volume(player, volume, &err);
477 if (err)
478 DAWarning("Error setting volume: %s", err->message);
479 g_clear_error(&err);
482 void ToggleTime(int x, int y, DARect rect, void *data)
484 if (t_time)
485 t_time = 0;
486 else t_time =1;
489 void buttonPress(int button, int state, int x, int y)
491 if (button==1)
492 left_pressed=1;
493 if (player)
495 if (button == 1)
497 char *tmp="1";
498 DAProcessActionRects(x, y, buttonRects, sizeof(buttonRects)/sizeof(DAActionRect), tmp);
499 DAProcessActionRects(x, y, displayRects, sizeof(displayRects)/sizeof(DAActionRect), tmp);
501 if (button == 2)
503 DAProcessActionRects(x, y, toggleRect, sizeof(toggleRect)/sizeof(DAActionRect), NULL);
505 if (button == 3)
507 char *tmp="1";
508 DAProcessActionRects(x, y, toggleRect, sizeof(toggleRect)/sizeof(DAActionRect), tmp);
510 if ((button == 4) || (button == 5))
512 if (button == 5)
514 /* Wheel scrolls down */
515 int tmp=2;
516 DAProcessActionRects(x, y, globRect, sizeof(globRect)/sizeof(DAActionRect), &tmp);
517 } else {
518 /* Wheel scrolls up */
519 int tmp=1;
520 DAProcessActionRects(x, y, globRect, sizeof(globRect)/sizeof(DAActionRect), &tmp);
524 else
525 DAProcessActionRects(x, y, toggleRect, sizeof(toggleRect)/sizeof(DAActionRect), NULL);
528 void buttonRelease(int button, int state, int x, int y)
530 if (button==1)
531 left_pressed=0;
532 if (player)
534 if (button == 1)
536 copyNumArea(0,51, 54, 20, 5,39);
537 DASetPixmap(pixmap);
538 DAProcessActionRects(x, y, buttonRects, sizeof(buttonRects)/sizeof(DAActionRect), NULL);
543 void buttonDrag(int x, int y)
545 motion_event=1;
546 if (left_pressed==1) {
547 DAProcessActionRects(x,y, volumeRects, 1, NULL);
548 DrawVolume();
549 DASetPixmap(pixmap);
553 int PlayerConnect(void)
555 GError *error = NULL;
556 static int previous_error_code = 0;
557 GList *players;
559 players = playerctl_list_players(&error);
560 if (error)
561 DAWarning("%s", error->message);
562 g_clear_error(&error);
564 /* check that current player is still connected */
565 if (player && CurrentPlayerIndex(players) == -1) {
566 DAWarning("Disconnected from player");
567 g_object_unref(player);
568 player = NULL;
571 if (!player && g_list_length(players) > 0) {
572 player = playerctl_player_new_from_name(
573 g_list_nth_data(players, 0), &error);
574 if (error != NULL) {
575 /* don't spam error message */
576 if (error->code != previous_error_code)
577 DAWarning("Connection to player failed: %s",
578 error->message);
579 previous_error_code = error->code;
580 } else {
581 char *player_name;
583 g_object_get(player, "player-name",
584 &player_name, NULL);
585 if (player_name)
586 DAWarning("Connected to %s", player_name);
587 g_free(player_name);
589 g_clear_error(&error);
590 } else
591 g_main_context_iteration(NULL, FALSE);
593 g_list_free_full(players,
594 (GDestroyNotify)playerctl_player_name_free);
596 return player ? 1 : 0;
599 void DisplayRoutine()
601 gint64 position = 0, length = 0;
602 int track_num = 100;
603 char *title = NULL;
604 GError *error = NULL;
606 PlayerConnect();
608 /* Compute diplay */
609 if (!player)
611 if (run_excusive)
612 exit(0);
613 title = strdup("--");
614 title_pos = 0;
615 arrow_pos = 5;
616 } else {
617 char *length_str, *track_num_str;
618 PlayerctlPlaybackStatus status;
620 g_object_get(player, "playback-status", &status, NULL);
621 if (status == PLAYERCTL_PLAYBACK_STATUS_PLAYING ||
622 status == PLAYERCTL_PLAYBACK_STATUS_PAUSED) {
623 if (status == PLAYERCTL_PLAYBACK_STATUS_PAUSED &&
624 pause_norotate)
625 arrow_pos = 5;
627 position = playerctl_player_get_position(player,
628 &error);
629 if (error != NULL)
630 DAWarning("%s", error->message);
631 g_clear_error(&error);
633 title = playerctl_player_get_title(player,
634 &error);
635 if (error != NULL)
636 DAWarning("%s", error->message);
637 g_clear_error(&error);
639 length_str =
640 playerctl_player_print_metadata_prop(
641 player, "mpris:length", &error);
642 if (error != NULL)
643 DAWarning("%s", error->message);
644 g_clear_error(&error);
645 if (length_str) {
646 length = g_ascii_strtoull(length_str,
647 NULL, 10);
648 g_free(length_str);
649 } else
650 length = 0;
652 track_num_str =
653 playerctl_player_print_metadata_prop(
654 player, "xesam:trackNumber",
655 &error);
656 if (error != NULL)
657 DAWarning("%s", error->message);
658 g_clear_error(&error);
659 if (track_num_str) {
660 track_num = atoi(track_num_str);
661 g_free(track_num_str);
662 } else
663 track_num = 0;
664 } else { /* not playing or paused */
665 title = strdup("--");
666 title_pos = 0;
667 arrow_pos = 5;
671 /*Draw everything */
672 DrawTime(t_time && length ? length - position : position);
673 DrawTrackNum(track_num);
674 DrawArrow();
675 DrawVolume();
676 DrawTitle(title);
678 DASetPixmap(pixmap);
680 if (title != NULL)
681 free(title);
684 void DrawTrackNum (int track_num)
686 char track_num_str[16];
687 char *p = track_num_str;
688 int i=1;
690 if (track_num > 99)
691 track_num = 0;
692 sprintf(track_num_str, "%02d", track_num);
694 for (;i<3; i++)
696 copyNumArea((*p-'0')*6 + 1, 1, 6, 7, (i*6)+39, 7);
697 p++;
701 void DrawTime(gint64 time)
703 char timestr[16];
704 char *p = timestr;
705 int i=0;
707 time /= 1000000;
709 /* 3 cases:
710 * up to 99 minutes and 59 seconds
711 * up to 99 hours and 59 minutes
712 * more
714 if (time < 6000)
716 int m = time / 60, s = time % 60;
718 sprintf(timestr, "%02d%02d", m, s);
720 else if (time < 360000)
722 int h = time / 3600, m = time % 3600;
724 sprintf(timestr, "%02d%02d", h, m);
726 else
727 strcpy (timestr, "0000");
729 for (;i<4; i++)
731 copyNumArea((*p-'0')*7 + 2, 11, 7, 9, i<2 ?(i*7)+7:(i*7)+12, 7);
732 p++;
736 void DrawArrow(void)
738 copyNumArea((arrow_pos*8)+30, 22, 8, 9, 47, 19);
739 arrow_pos++;
740 if (arrow_pos > 4) arrow_pos = 0;
743 void DrawVolume(void)
745 int volume;
746 double volume_double = 0.0;
748 if (player)
749 g_object_get(player, "volume", &volume_double, NULL);
751 volume = (int)(36 * volume_double);
752 if (volume > 36)
753 volume = 36;
754 copyNumArea(61, 0, volume, 6, 7, 18);
755 copyNumArea(98, 0, 36-volume, 6, 7+volume, 18);
758 void DrawKbps(int bps)
760 char kbpstr[16];
761 char *p = kbpstr;
762 int i=1;
764 if (bps > 999000) bps=0;
765 sprintf(kbpstr, "%03d", bps / 1000);
767 for (;i<4; i++)
769 copyNumArea((*p-'0')*6 + 1, 1, 6, 7, (i*6)+1, 26);
770 p++;
772 copyNumArea(55, 39, 18, 8, 25, 26);
775 void DrawChar(wchar_t wc, int x, int y)
777 int i;
778 for(i = 0; i < sizeof(glyphs)/sizeof(glyphdescr) - 1; ++i)
780 if(wc == glyphs[i].c)
781 break;
783 copyNumArea(glyphs[i].x, glyphs[i].y, 6, 8, x, y);
786 int DrawChars(char *title, int tpos, int pos)
788 wchar_t wc;
790 mbtowc(NULL, NULL, 0);
791 while(*title && (pos <= (tpos + DISPLAYSIZE)))
793 int len = mbtowc(&wc, title, MB_CUR_MAX);
794 title += len;
795 if(pos >= tpos)
796 DrawChar(wc, (pos - tpos)*6 + 7, 26);
797 ++pos;
799 return pos;
802 void DrawTitle(char *name)
804 int len, pos, tpos = title_pos;
806 if (name == NULL)
807 return;
809 len = pos = DrawChars(name, tpos, 0);
811 if(pos < 6)
813 DrawChars(" ", tpos, pos);
814 return;
817 if(pos <= tpos + DISPLAYSIZE)
818 pos = DrawChars(SEPARATOR, tpos, pos);
820 if(pos <= tpos + DISPLAYSIZE)
821 DrawChars(name, tpos, pos);
823 if(tpos >= len + strlen(SEPARATOR))
824 title_pos = 0;
826 title_pos = title_pos + 0.5;
829 void ExecuteXmms(void)
831 char *command;
832 int status;
834 command=malloc(strlen(xmms_cmd)+5);
835 sprintf(command, "%s &", xmms_cmd);
836 status = system(command);
837 if (status)
839 fprintf(stderr, "XMMS can't be launched, exiting...");
840 exit(1);
842 while (!PlayerConnect())
843 usleep(10000L);
844 free(command);
846 /*----------------------------------------------------------------------------*/
847 /* Main */
848 /*----------------------------------------------------------------------------*/
850 int main(int argc, char **argv)
852 short unsigned int height, width;
853 DACallbacks callbacks={NULL, buttonPress, buttonRelease, buttonDrag,
854 NULL, NULL, NULL};
856 /* Initialization */
857 DAParseArguments(argc, argv, options,
858 sizeof(options)/sizeof(DAProgramOption),
859 "MPRIS-compatible media player remote control\n"
860 "by Bastien Nocera <hadess@hadess.net>\n"
861 "maintained by the Window Maker Team "
862 "<wmaker-dev@googlegroups.com>",
863 PACKAGE_STRING);
865 setlocale(LC_ALL, "");
866 DAInitialize(displayName, "wmusic", 64, 64, argc, argv);
867 DASetCallbacks(&callbacks);
868 DASetTimeout(100);
870 DAMakePixmapFromData(wmusic_master_xpm, &pixmap,
871 &mask, &height, &width);
872 DAMakePixmapFromData(wmusic_digits_xpm, &pixnum,
873 &masknum, &height, &width);
874 gc = DefaultGC(DADisplay, DefaultScreen(DADisplay));
875 DASetShapeWithOffset(mask, 0, 0);
877 DASetPixmap(pixmap);
878 DAShow();
880 /* End of initialization */
882 if (options[4].used) pause_norotate=1;
883 if (options[5].used) t_time=1;
884 if (options[6].used) run_excusive=1;
886 PlayerConnect();
888 /* Launch xmms if it's not running and -r or -R was used */
889 if ((!player) && (options[2].used || run_excusive))
891 ExecuteXmms();
894 /* Update the display */
895 DisplayRoutine();
897 while (1) {
898 if (DANextEventOrTimeout(&ev, 100))
899 DAProcessEvent(&ev);
900 if (!motion_event)
901 DisplayRoutine();
902 else
903 motion_event=0;
905 return 0;