2 * Copyright (C) 2003-2006 Andre Noll <maan@systemlinux.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
19 /** \file sdl_gui.c SDL-based interface for paraslash */
28 #include <SDL/SDL_image.h>
29 #include <SDL/SDL_events.h>
30 #include <SDL/SDL_keyboard.h>
31 #include <SDL/SDL_keysym.h>
32 #include <sys/time.h> /* timeval, needed for select */
34 #include "sdl_gui.cmdline.h"
37 #define FRAME_GREEN 200
38 #define FRAME_BLUE 200
42 static int height = 0;
44 extern const char *status_item_list[NUM_STAT_ITEMS];
45 struct gengetopt_args_info args_info;
47 #define FRAME_WIDTH 10
48 #define FONT_HEIGHT 36
50 #define INPUT_WIDTH width - 2 * FRAME_WIDTH
51 #define INPUT_X FRAME_WIDTH
52 #define INPUT_HEIGHT (args_info.interactive_flag? FONT_HEIGHT : 0)
53 #define INPUT_Y height - FRAME_WIDTH - INPUT_HEIGHT
55 #define OUTPUT_WIDTH width - 2 * FRAME_WIDTH
56 #define OUTPUT_X FRAME_WIDTH
57 #define OUTPUT_Y FRAME_WIDTH
58 #define OUTPUT_HEIGHT (height - INPUT_HEIGHT - 2 * FRAME_WIDTH)
60 #define NUM_LINES (height - 2 * FRAME_WIDTH - INPUT_HEIGHT) / FONT_HEIGHT
88 SFont_FontInfo fontinfo;
91 struct font fonts[] = {
93 .name = "24P_Arial_Metallic_Yellow.png",
94 .fontinfo = {NULL, {0}, 0}
97 .name = "24P_Arial_NeonYellow.png",
98 .fontinfo = {NULL, {0}, 0}
101 .name = "24P_Copperplate_Blue.png",
102 .fontinfo = {NULL, {0}, 0}
106 .fontinfo = {NULL, {0}, 0}
111 #define PIC_WIDTH width * 3 / 10
112 #define PIC_HEIGHT height / 3
115 #define OUTPUT_FONT 1
119 static struct stat_item stat_items[NUM_STAT_ITEMS];
121 void para_log(__unused int ll, __unused char* fmt,...) /* no logging */
125 static void init_stat_items(void)
128 struct stat_item *s = stat_items;
130 for (i = 0; i < NUM_STAT_ITEMS; i++) {
136 s[SI_STATUS_BAR].prefix = "";
137 s[SI_STATUS_BAR].postfix = "";
138 s[SI_STATUS_BAR].content = "";
139 s[SI_STATUS_BAR].x = 0;
140 s[SI_STATUS_BAR].y = 10;
141 s[SI_STATUS_BAR].w = 100;
142 s[SI_STATUS_BAR].h = FONT_HEIGHT;
143 s[SI_STATUS_BAR].r = 0;
144 s[SI_STATUS_BAR].g = 0;
145 s[SI_STATUS_BAR].b = 0;
146 s[SI_STATUS_BAR].font = M_YELLOW;
147 s[SI_STATUS_BAR].align = CENTER;
149 s[SI_PLAY_TIME].prefix = "";
150 s[SI_PLAY_TIME].postfix = "";
151 s[SI_PLAY_TIME].content = "";
152 s[SI_PLAY_TIME].x = 35;
153 s[SI_PLAY_TIME].y = 20;
154 s[SI_PLAY_TIME].w = 65;
155 s[SI_PLAY_TIME].h = FONT_HEIGHT;
156 s[SI_PLAY_TIME].r = 0;
157 s[SI_PLAY_TIME].g = 0;
158 s[SI_PLAY_TIME].b = 0;
159 s[SI_PLAY_TIME].font = M_YELLOW;
160 s[SI_PLAY_TIME].align = CENTER;
162 s[SI_STATUS].prefix = "";
163 s[SI_STATUS].postfix = "";
164 s[SI_STATUS].content = "";
168 s[SI_STATUS].h = FONT_HEIGHT;
172 s[SI_STATUS].font = N_YELLOW;
173 s[SI_STATUS].align = LEFT;
175 s[SI_STATUS_FLAGS].prefix = " (";
176 s[SI_STATUS_FLAGS].postfix = ")";
177 s[SI_STATUS_FLAGS].content = "";
178 s[SI_STATUS_FLAGS].x = 47;
179 s[SI_STATUS_FLAGS].y = 28;
180 s[SI_STATUS_FLAGS].w = 15;
181 s[SI_STATUS_FLAGS].h = FONT_HEIGHT;
182 s[SI_STATUS_FLAGS].r = 0;
183 s[SI_STATUS_FLAGS].g = 0;
184 s[SI_STATUS_FLAGS].b = 0;
185 s[SI_STATUS_FLAGS].font = N_YELLOW;
186 s[SI_STATUS_FLAGS].align = CENTER;
188 s[SI_NUM_PLAYED].prefix = "#";
189 s[SI_NUM_PLAYED].postfix = "";
190 s[SI_NUM_PLAYED].content = "0";
191 s[SI_NUM_PLAYED].x = 62;
192 s[SI_NUM_PLAYED].y = 28;
193 s[SI_NUM_PLAYED].w = 13;
194 s[SI_NUM_PLAYED].h = FONT_HEIGHT;
195 s[SI_NUM_PLAYED].r = 0;
196 s[SI_NUM_PLAYED].g = 0;
197 s[SI_NUM_PLAYED].b = 0;
198 s[SI_NUM_PLAYED].font = N_YELLOW;
199 s[SI_NUM_PLAYED].align = CENTER;
201 s[SI_UPTIME].prefix = "Up: ";
202 s[SI_UPTIME].postfix = "";
203 s[SI_UPTIME].content = "";
207 s[SI_UPTIME].h = FONT_HEIGHT;
211 s[SI_UPTIME].font = N_YELLOW;
212 s[SI_UPTIME].align = RIGHT;
214 s[SI_DBTOOL].prefix = "dbtool: ";
215 s[SI_DBTOOL].postfix = "";
216 s[SI_DBTOOL].content = "no content yet";
220 s[SI_DBTOOL].h = FONT_HEIGHT;
224 s[SI_DBTOOL].font = N_YELLOW;
225 s[SI_DBTOOL].align = LEFT;
227 s[SI_FORMAT].prefix = "Format: ";
228 s[SI_FORMAT].postfix = "";
229 s[SI_FORMAT].content = "";
233 s[SI_FORMAT].h = FONT_HEIGHT;
237 s[SI_FORMAT].font = N_YELLOW;
238 s[SI_FORMAT].align = RIGHT;
240 s[SI_MTIME].prefix = "MTime: ";
241 s[SI_MTIME].postfix = "";
242 s[SI_MTIME].content = "";
246 s[SI_MTIME].h = FONT_HEIGHT;
250 s[SI_MTIME].font = N_YELLOW;
251 s[SI_MTIME].align = LEFT;
253 s[SI_FILE_SIZE].prefix = "Size: ";
254 s[SI_FILE_SIZE].postfix = "kb";
255 s[SI_FILE_SIZE].content = "";
256 s[SI_FILE_SIZE].x = 35;
257 s[SI_FILE_SIZE].y = 42;
258 s[SI_FILE_SIZE].w = 20;
259 s[SI_FILE_SIZE].h = FONT_HEIGHT;
260 s[SI_FILE_SIZE].r = 0;
261 s[SI_FILE_SIZE].g = 0;
262 s[SI_FILE_SIZE].b = 0;
263 s[SI_FILE_SIZE].font = N_YELLOW;
264 s[SI_FILE_SIZE].align = LEFT;
266 s[SI_AUDIO_INFO1].prefix = "";
267 s[SI_AUDIO_INFO1].postfix = "";
268 s[SI_AUDIO_INFO1].content = "";
269 s[SI_AUDIO_INFO1].x = 0;
270 s[SI_AUDIO_INFO1].y = 60;
271 s[SI_AUDIO_INFO1].w = 100;
272 s[SI_AUDIO_INFO1].h = FONT_HEIGHT;
273 s[SI_AUDIO_INFO1].r = 0;
274 s[SI_AUDIO_INFO1].g = 0;
275 s[SI_AUDIO_INFO1].b = 0;
276 s[SI_AUDIO_INFO1].font = N_YELLOW;
277 s[SI_AUDIO_INFO1].align = CENTER;
279 s[SI_AUDIO_INFO2].prefix = "";
280 s[SI_AUDIO_INFO2].postfix = "";
281 s[SI_AUDIO_INFO2].content = "";
282 s[SI_AUDIO_INFO2].x = 0;
283 s[SI_AUDIO_INFO2].y = 65;
284 s[SI_AUDIO_INFO2].w = 100;
285 s[SI_AUDIO_INFO2].h = FONT_HEIGHT;
286 s[SI_AUDIO_INFO2].r = 0;
287 s[SI_AUDIO_INFO2].g = 0;
288 s[SI_AUDIO_INFO2].b = 0;
289 s[SI_AUDIO_INFO2].font = N_YELLOW;
290 s[SI_AUDIO_INFO2].align = CENTER;
292 s[SI_AUDIO_INFO3].prefix = "";
293 s[SI_AUDIO_INFO3].postfix = "";
294 s[SI_AUDIO_INFO3].content = "";
295 s[SI_AUDIO_INFO3].x = 0;
296 s[SI_AUDIO_INFO3].y = 70;
297 s[SI_AUDIO_INFO3].w = 100;
298 s[SI_AUDIO_INFO3].h = FONT_HEIGHT;
299 s[SI_AUDIO_INFO3].r = 0;
300 s[SI_AUDIO_INFO3].g = 0;
301 s[SI_AUDIO_INFO3].b = 0;
302 s[SI_AUDIO_INFO3].font = N_YELLOW;
303 s[SI_AUDIO_INFO3].align = CENTER;
305 s[SI_DBINFO1].name = "dbinfo1:";
306 s[SI_DBINFO1].prefix = "";
307 s[SI_DBINFO1].postfix = "";
308 s[SI_DBINFO1].content = "";
310 s[SI_DBINFO1].y = 83;
311 s[SI_DBINFO1].w = 100;
312 s[SI_DBINFO1].h = FONT_HEIGHT;
316 s[SI_DBINFO1].font = N_YELLOW;
317 s[SI_DBINFO1].align = CENTER;
319 s[SI_DBINFO2].prefix = "";
320 s[SI_DBINFO2].postfix = "";
321 s[SI_DBINFO2].content = "";
323 s[SI_DBINFO2].y = 88;
324 s[SI_DBINFO2].w = 100;
325 s[SI_DBINFO2].h = FONT_HEIGHT;
329 s[SI_DBINFO2].font = N_YELLOW;
330 s[SI_DBINFO2].align = CENTER;
332 s[SI_DBINFO3].name = "dbinfo3:";
333 s[SI_DBINFO3].prefix = "";
334 s[SI_DBINFO3].postfix = "";
335 s[SI_DBINFO3].content = "";
337 s[SI_DBINFO3].y = 93;
338 s[SI_DBINFO3].w = 100;
339 s[SI_DBINFO3].h = FONT_HEIGHT;
343 s[SI_DBINFO3].font = N_YELLOW;
344 s[SI_DBINFO3].align = CENTER;
348 * init SDL libary and set window title
350 static void init_SDL(void)
352 if (SDL_Init(SDL_INIT_VIDEO) == -1) {
354 "Couldn't initialize SDL: %s\n", SDL_GetError());
357 /* Clean up on exit */
359 /* Initialize the display */
360 if (args_info.fullscreen_flag)
361 screen = SDL_SetVideoMode(width, height, 0, SDL_FULLSCREEN);
363 screen = SDL_SetVideoMode(width, height, 0, 0);
365 fprintf(stderr, "Couldn't set video mode: %s\n",
369 SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
370 SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
371 SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
372 /* Set the window manager title bar */
373 SDL_WM_SetCaption("The Gui of death that makes you blind (paraslash "
374 VERSION ")", "SFont");
378 * draw rectangular frame of width FRAME_WIDTH
380 static void draw_frame(Uint8 r, Uint8 g, Uint8 b) {
386 rect.h = FRAME_WIDTH;
387 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
388 SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
391 rect.y = height - FRAME_WIDTH;
393 rect.h = FRAME_WIDTH;
394 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
395 SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
398 rect.y = FRAME_WIDTH;
399 rect.w = FRAME_WIDTH;
400 rect.h = height - 2 * FRAME_WIDTH;
401 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
402 SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
404 rect.x = width - FRAME_WIDTH;
405 rect.y = FRAME_WIDTH;
406 rect.w = FRAME_WIDTH;
407 rect.h = height - 2 * FRAME_WIDTH;
408 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
409 SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
413 * fill input rect with color
415 static void fill_input_rect(void)
421 rect.w = INPUT_WIDTH;
422 rect.h = INPUT_HEIGHT;
423 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 10, 150, 10));
427 * fill output rect with color
429 static void fill_output_rect(void)
435 rect.w = OUTPUT_WIDTH;
436 rect.h = OUTPUT_HEIGHT;
437 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
441 * convert tab to space
443 static void tab2space(char *text)
453 static void print_msg(char *msg)
455 SFont_FontInfo *font = &(fonts[MSG_FONT].fontinfo);
456 char *buf = strdup(msg);
457 int len = strlen(buf);
461 while (TextWidth2(font, buf) > INPUT_WIDTH && len > 0) {
466 PutString2(screen, font, INPUT_X, INPUT_Y, buf);
470 static void update_all(void)
472 SDL_UpdateRect(screen, 0, 0, 0, 0);
475 static void update_input(void)
477 SDL_UpdateRect(screen, INPUT_X, INPUT_Y, INPUT_WIDTH, INPUT_HEIGHT);
482 * wait for key, ignore all other events, return 0 if there is no key event
483 * pending. Otherwise return keysym of key
489 while (SDL_PollEvent(&event) > 0) {
490 if(event.type != SDL_KEYDOWN)
492 // printf("Key pressed, scancode: 0x%x\n",
493 // event.key.keysym.scancode);
494 return event.key.keysym.sym;
500 * print message, wait for key (blocking), return 1 for 'q', 0 else
502 static SDLKey hit_key(char *msg)
508 while (!(sym = get_key()))
519 * read paraslash command from input, execute it and print results
521 static int command_handler(void)
525 char text[MAXLINE]="";
526 char buf[MAXLINE]="";
527 SFont_FontInfo *font = &fonts[OUTPUT_FONT].fontinfo;
529 // printf("string input\n");
530 SFont_Input2(screen, &fonts[INPUT_FONT].fontinfo,
531 INPUT_X, INPUT_Y - 5, INPUT_WIDTH, text);
534 if (!strcmp(text, "exit") || !strcmp(text, "quit"))
536 if (text[0] == '!') {
539 pipe = popen(text + 1, "r");
541 sprintf(buf, BINDIR "/para_client %s 2>&1", text);
542 pipe = popen(buf, "r");
547 while(fgets(text, MAXLINE - 1, pipe)) {
552 // printf("string: %s\n", dest);
553 while (TextWidth2(font, text) > width - 2 * FRAME_WIDTH &&
558 PutString2(screen, font, OUTPUT_X,
559 OUTPUT_Y + count * FONT_HEIGHT, text);
561 if (count >= NUM_LINES) {
563 if (hit_key("Hit any key to continue, q to return"))
570 hit_key("Hit any key to return");
571 out: fill_output_rect();
578 * Add prefix and postfix to string, delete characters from the end
579 * if its length exceeds the max length defined in stat_items[item]
581 char *transform_string(int item)
583 struct stat_item s = stat_items[item];
586 unsigned pixels = s.w * (width - 2 * FRAME_WIDTH) / 100;
587 SFont_FontInfo *font = &(fonts[s.font].fontinfo);
589 ret = make_message("%s%s%s", s.prefix, s.content, s.postfix);
591 while (TextWidth2(font, ret) > pixels && len > 0) {
598 SDL_Surface *load_jpg(void)
601 int fds[3] = {0, 1, 0};
605 if (para_exec_cmdline_pid(&pid, args_info.pic_cmd_arg, fds) < 0)
607 pipe = fdopen(fds[1], "r");
610 if (!(rwop = SDL_RWFromFP(pipe, 0)))
612 return IMG_LoadJPG_RW(rwop);
615 void update_pic(void)
618 SDL_Rect src_pic_rect = {
624 SDL_Rect dest_pic_rect = {
626 .y = OUTPUT_HEIGHT / 5,
634 if (!(img = load_jpg()))
636 SDL_FillRect(screen, &dest_pic_rect, SDL_MapRGB(screen->format,
638 SDL_BlitSurface(img, &src_pic_rect, screen, &dest_pic_rect);
640 SDL_FreeSurface(img);
644 * update status item number i.
646 static void do_update(int i)
648 static int last_played = -1;
651 SFont_FontInfo *font = &(fonts[stat_items[i].font].fontinfo);
652 if (!stat_items[i].w)
655 rect.x = stat_items[i].x * (width - FRAME_WIDTH * 2) / 100
657 rect.y = stat_items[i].y * (height - 2 * FRAME_WIDTH - INPUT_HEIGHT)
659 rect.w = stat_items[i].w * (width - 2 * FRAME_WIDTH) / 100;
660 rect.h = stat_items[i].h;
661 buf = transform_string(i);
662 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format,
663 stat_items[i].r, stat_items[i].g, stat_items[i].b));
664 switch(stat_items[i].align) {
666 PutString2(screen, font,
667 rect.x + (rect.w - TextWidth2(font, buf)) / 2,
671 PutString2(screen, font, rect.x, rect.y, buf);
674 PutString2(screen, font, rect.x + (rect.w -
675 TextWidth2(font, buf)), rect.y, buf);
679 SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
680 if (i == SI_NUM_PLAYED && atoi(stat_items[i].content) != last_played) {
682 last_played = atoi(stat_items[i].content);
687 * Check if buf is a known status line. If so call do_update and return 1.
688 * Return 0 otherwise.
690 void update_status(char *buf)
694 i = stat_line_valid(buf);
697 //free(stat_items[i].content);
698 stat_items[i].content = para_strdup(buf +
699 strlen(status_item_list[i]) + 1);
704 * Read stat line from pipe if pipe is ready, call update_status to
705 * display information.
707 static int draw_status(int pipe)
714 tv.tv_usec = 3000000;
717 ret = select(pipe + 1, &rfds, NULL, NULL, &tv);
718 // printf("select returned %d\n", ret);
721 if (read_audiod_pipe(pipe, update_status) > 0)
723 // clear_all_items();
724 free(stat_items[SI_STATUS_BAR].content);
725 stat_items[SI_STATUS_BAR].content =
726 para_strdup("audiod not running!?\n");
732 static void clean_exit(int ret)
738 static void print_help(void)
740 print_msg("Hit q to quit, any other key to enter command mode");
743 static int configfile_exists(void)
745 if (!args_info.config_file_given) {
746 char *home = para_homedir();
747 args_info.config_file_arg = make_message(
748 "%s/.paraslash/sdl_gui.conf", home);
751 return file_exists(args_info.config_file_arg);
757 int main(int argc, char *argv[])
762 cmdline_parser(argc, argv, &args_info);
763 ret = configfile_exists();
764 // printf("w=%i,h=%i,ret=%i, cf=%s\n", width, height, ret, args_info.config_file_arg);
766 if (!ret && args_info.config_file_given) {
767 fprintf(stderr, "Can't read config file %s\n",
768 args_info.config_file_arg);
772 cmdline_parser_configfile(args_info.config_file_arg,
773 &args_info, 0, 0, 0);
774 signal(SIGCHLD, SIG_IGN);
775 width = args_info.width_arg;
776 height = args_info.height_arg;
777 // printf("w=%i,h=%i,ret=%i, cf=%s\n", width, height, ret, args_info.config_file_arg);
779 pipe = para_open_audiod_pipe(args_info.stat_cmd_arg);
781 for (i = 0; fonts[i].name[0]; i++) {
783 sprintf(buf, "%s/%s", FONTDIR, fonts[i].name);
784 /* Load the font - You don't have to use the IMGlib for this */
785 fonts[i].fontinfo.Surface = IMG_Load(buf);
786 /* Prepare the font for use */
787 InitFont2(&fonts[i].fontinfo);
789 draw_frame(FRAME_RED, FRAME_GREEN, FRAME_BLUE);
790 if (args_info.interactive_flag) {
795 ret = draw_status(pipe);
800 if (SDL_QuitRequested())
802 while ((sym = get_key())) {
803 if (!args_info.interactive_flag)
807 if ( sym == SDLK_LSHIFT
808 || sym == SDLK_RSHIFT
814 || sym == SDLK_CAPSLOCK
817 || sym == SDLK_RSUPER
818 || sym == SDLK_LSUPER
819 || sym == SDLK_COMPOSE
823 // printf("closing pipe\n");
826 // printf("pipe closed\n");
830 if (!command_handler())
835 SDL_UpdateRect(screen, 0, 0, 0, 0);
836 pipe = para_open_audiod_pipe(args_info.stat_cmd_arg);