| File src/console.c changed (mode: 100644) (index 3431e0b..7fcf19c) | 
	| 1 |  | /* vi:set ts=4 sts=4 sw=4 noet: |  | 
	|  | 1 |  | /* vi:set ts=4 sts=4 sw=4 et: | 
	| 2 | 2 | * | * | 
	| 3 | 3 | * Practical Music Search | * Practical Music Search | 
	| 4 | 4 | * Copyright (c) 2006-2014 Kim Tore Jensen | * Copyright (c) 2006-2014 Kim Tore Jensen | 
|  | 
	| ... | ... | pthread_mutex_t console_mutex = PTHREAD_MUTEX_INITIALIZER; | 
	| 35 | 35 | window_t * console_window = NULL; | window_t * console_window = NULL; | 
	| 36 | 36 |  |  | 
	| 37 | 37 | void console_resize() { | void console_resize() { | 
	| 38 |  | console_window->height = LINES - 2; |  | 
	| 39 |  | console_window->position = 0; |  | 
	| 40 |  | console_window->num_lines = 0; |  | 
	|  | 38 |  | console_window->height = LINES - 2; | 
	|  | 39 |  | console_window->position = 0; | 
	|  | 40 |  | console_window->num_lines = 0; | 
	| 41 | 41 | } | } | 
	| 42 | 42 |  |  | 
	| 43 | 43 | void console_init(unsigned int max_lines) { | void console_init(unsigned int max_lines) { | 
	| 44 |  | if (lines != NULL) { |  | 
	| 45 |  | lines = realloc(lines, max_lines * sizeof(logline_t *)); |  | 
	| 46 |  | } else { |  | 
	| 47 |  | lines = malloc(max_lines * sizeof(logline_t *)); |  | 
	| 48 |  | } |  | 
	| 49 |  | if (lines == NULL) { |  | 
	| 50 |  | fatal(PMS_EXIT_MEMORY, "out of memory\n"); |  | 
	| 51 |  | } |  | 
	| 52 |  | if (console_window == NULL) { |  | 
	| 53 |  | console_window = malloc(sizeof(window_t)); |  | 
	| 54 |  | if (console_window == NULL) { |  | 
	| 55 |  | fatal(PMS_EXIT_MEMORY, "out of memory\n"); |  | 
	| 56 |  | } |  | 
	| 57 |  | console_resize(); |  | 
	| 58 |  | } |  | 
	| 59 |  | memset(lines+line_limit, 0, (max_lines-line_limit)*sizeof(logline_t *)); |  | 
	| 60 |  | line_limit = max_lines; |  | 
	|  | 44 |  | if (lines != NULL) { | 
	|  | 45 |  | lines = realloc(lines, max_lines * sizeof(logline_t *)); | 
	|  | 46 |  | } else { | 
	|  | 47 |  | lines = malloc(max_lines * sizeof(logline_t *)); | 
	|  | 48 |  | } | 
	|  | 49 |  | if (lines == NULL) { | 
	|  | 50 |  | fatal(PMS_EXIT_MEMORY, "out of memory\n"); | 
	|  | 51 |  | } | 
	|  | 52 |  | if (console_window == NULL) { | 
	|  | 53 |  | console_window = malloc(sizeof(window_t)); | 
	|  | 54 |  | if (console_window == NULL) { | 
	|  | 55 |  | fatal(PMS_EXIT_MEMORY, "out of memory\n"); | 
	|  | 56 |  | } | 
	|  | 57 |  | console_resize(); | 
	|  | 58 |  | } | 
	|  | 59 |  | memset(lines+line_limit, 0, (max_lines-line_limit)*sizeof(logline_t *)); | 
	|  | 60 |  | line_limit = max_lines; | 
	| 61 | 61 | } | } | 
	| 62 | 62 |  |  | 
	| 63 | 63 | void console(const char * format, ...) { | void console(const char * format, ...) { | 
	| 64 |  | logline_t *	line; |  | 
	| 65 |  | time_t		t; |  | 
	| 66 |  | va_list		ap; |  | 
	|  | 64 |  | logline_t *    line; | 
	|  | 65 |  | time_t        t; | 
	|  | 66 |  | va_list        ap; | 
	| 67 | 67 |  |  | 
	| 68 |  | pthread_mutex_lock(&console_mutex); |  | 
	|  | 68 |  | pthread_mutex_lock(&console_mutex); | 
	| 69 | 69 |  |  | 
	| 70 |  | line = new_logline(); |  | 
	|  | 70 |  | line = new_logline(); | 
	| 71 | 71 |  |  | 
	| 72 |  | va_start(ap, format); |  | 
	| 73 |  | vsnprintf(line->str, 512, format, ap); |  | 
	| 74 |  | va_end(ap); |  | 
	|  | 72 |  | va_start(ap, format); | 
	|  | 73 |  | vsnprintf(line->str, 512, format, ap); | 
	|  | 74 |  | va_end(ap); | 
	| 75 | 75 |  |  | 
	| 76 |  | if (lines[line_cursor] != NULL) { |  | 
	| 77 |  | free_logline(lines[line_cursor]); |  | 
	| 78 |  | } |  | 
	|  | 76 |  | if (lines[line_cursor] != NULL) { | 
	|  | 77 |  | free_logline(lines[line_cursor]); | 
	|  | 78 |  | } | 
	| 79 | 79 |  |  | 
	| 80 |  | ++console_window->num_lines; |  | 
	|  | 80 |  | ++console_window->num_lines; | 
	| 81 | 81 |  |  | 
	| 82 |  | t = time(NULL); |  | 
	| 83 |  | localtime_r(&t, &line->timestamp); |  | 
	| 84 |  | strftime(line->ts, 9, "%H:%M:%S", &line->timestamp); |  | 
	|  | 82 |  | t = time(NULL); | 
	|  | 83 |  | localtime_r(&t, &line->timestamp); | 
	|  | 84 |  | strftime(line->ts, 9, "%H:%M:%S", &line->timestamp); | 
	| 85 | 85 |  |  | 
	| 86 |  | lines[line_cursor] = line; |  | 
	|  | 86 |  | lines[line_cursor] = line; | 
	| 87 | 87 |  |  | 
	| 88 |  | if (++line_cursor >= line_limit) { |  | 
	| 89 |  | console_init(line_limit*2); |  | 
	| 90 |  | } |  | 
	|  | 88 |  | if (++line_cursor >= line_limit) { | 
	|  | 89 |  | console_init(line_limit*2); | 
	|  | 90 |  | } | 
	| 91 | 91 |  |  | 
	| 92 |  | /* Scroll window if at bottom. */ |  | 
	| 93 |  | if (console_window->num_lines < console_window->height || console_window->position + console_window->height + 1 >= console_window->num_lines) { |  | 
	| 94 |  | console_scroll(1); |  | 
	| 95 |  | } |  | 
	|  | 92 |  | /* Scroll window if at bottom. */ | 
	|  | 93 |  | if (console_window->num_lines < console_window->height || console_window->position + console_window->height + 1 >= console_window->num_lines) { | 
	|  | 94 |  | console_scroll(1); | 
	|  | 95 |  | } | 
	| 96 | 96 |  |  | 
	| 97 |  | pthread_mutex_unlock(&console_mutex); |  | 
	|  | 97 |  | pthread_mutex_unlock(&console_mutex); | 
	| 98 | 98 | } | } | 
	| 99 | 99 |  |  | 
	| 100 | 100 | void console_draw_lines(long start, long end) { | void console_draw_lines(long start, long end) { | 
	| 101 | 101 |  |  | 
	| 102 |  | long s; |  | 
	| 103 |  | long ptr; |  | 
	| 104 |  | logline_t * line; |  | 
	|  | 102 |  | long s; | 
	|  | 103 |  | long ptr; | 
	|  | 104 |  | logline_t * line; | 
	| 105 | 105 |  |  | 
	| 106 | 106 | pms_curses_lock(); | pms_curses_lock(); | 
	| 107 | 107 |  |  | 
	| 108 |  | ptr = console_window->position + start; |  | 
	| 109 |  | for (s = start; s <= end; s++) { |  | 
	| 110 |  | if (ptr >= console_window->num_lines) { |  | 
	| 111 |  | break; |  | 
	| 112 |  | } |  | 
	| 113 |  | line = lines[ptr]; |  | 
	| 114 |  | mvwprintw(window_main, s, 0, "%s: %s", line->ts, line->str); |  | 
	| 115 |  | ++ptr; |  | 
	| 116 |  | } |  | 
	|  | 108 |  | ptr = console_window->position + start; | 
	|  | 109 |  | for (s = start; s <= end; s++) { | 
	|  | 110 |  | if (ptr >= console_window->num_lines) { | 
	|  | 111 |  | break; | 
	|  | 112 |  | } | 
	|  | 113 |  | line = lines[ptr]; | 
	|  | 114 |  | mvwprintw(window_main, s, 0, "%s: %s", line->ts, line->str); | 
	|  | 115 |  | ++ptr; | 
	|  | 116 |  | } | 
	| 117 | 117 |  |  | 
	| 118 |  | wrefresh(window_main); |  | 
	|  | 118 |  | wrefresh(window_main); | 
	| 119 | 119 |  |  | 
	| 120 | 120 | pms_curses_unlock(); | pms_curses_unlock(); | 
	| 121 | 121 | } | } | 
	| 122 | 122 |  |  | 
	| 123 | 123 | int console_scroll(long delta) { | int console_scroll(long delta) { | 
	| 124 | 124 |  |  | 
	| 125 |  | int changed = window_scroll(console_window, delta); |  | 
	|  | 125 |  | int changed = window_scroll(console_window, delta); | 
	| 126 | 126 |  |  | 
	| 127 |  | if (changed > 0) { |  | 
	| 128 |  | console_draw_lines(console_window->height - changed, console_window->height); |  | 
	| 129 |  | } else if (changed < 0) { |  | 
	| 130 |  | console_draw_lines(0, -changed - 1); |  | 
	| 131 |  | } else { |  | 
	| 132 |  | console_draw_lines(console_window->num_lines - 1, console_window->num_lines - 1); |  | 
	| 133 |  | } |  | 
	|  | 127 |  | if (changed > 0) { | 
	|  | 128 |  | console_draw_lines(console_window->height - changed, console_window->height); | 
	|  | 129 |  | } else if (changed < 0) { | 
	|  | 130 |  | console_draw_lines(0, -changed - 1); | 
	|  | 131 |  | } else { | 
	|  | 132 |  | console_draw_lines(console_window->num_lines - 1, console_window->num_lines - 1); | 
	|  | 133 |  | } | 
	| 134 | 134 |  |  | 
	| 135 |  | return changed; |  | 
	|  | 135 |  | return changed; | 
	| 136 | 136 | } | } | 
	| 137 | 137 |  |  | 
	| 138 | 138 | int console_scroll_to(long position) { | int console_scroll_to(long position) { | 
	| 139 |  | if (position < 0) { |  | 
	| 140 |  | position = console_window->num_lines+position; |  | 
	| 141 |  | } |  | 
	| 142 |  | position -= console_window->position; |  | 
	| 143 |  | console_scroll(position); |  | 
	|  | 139 |  | if (position < 0) { | 
	|  | 140 |  | position = console_window->num_lines+position; | 
	|  | 141 |  | } | 
	|  | 142 |  | position -= console_window->position; | 
	|  | 143 |  | console_scroll(position); | 
	| 144 | 144 | } | } | 
	| 145 | 145 |  |  | 
	| 146 | 146 | logline_t * new_logline() { | logline_t * new_logline() { | 
	| 147 |  | logline_t * line; |  | 
	|  | 147 |  | logline_t * line; | 
	| 148 | 148 |  |  | 
	| 149 |  | if ((line = malloc(sizeof(logline_t))) == NULL) { |  | 
	| 150 |  | fatal(PMS_EXIT_MEMORY, "Out of memory"); |  | 
	| 151 |  | } |  | 
	|  | 149 |  | if ((line = malloc(sizeof(logline_t))) == NULL) { | 
	|  | 150 |  | fatal(PMS_EXIT_MEMORY, "Out of memory"); | 
	|  | 151 |  | } | 
	| 152 | 152 |  |  | 
	| 153 |  | line->str = malloc(512); |  | 
	| 154 |  | line->ts = malloc(9); |  | 
	|  | 153 |  | line->str = malloc(512); | 
	|  | 154 |  | line->ts = malloc(9); | 
	| 155 | 155 |  |  | 
	| 156 |  | if (line->str == NULL || line->ts == NULL) { |  | 
	| 157 |  | fatal(PMS_EXIT_MEMORY, "Out of memory"); |  | 
	| 158 |  | } |  | 
	|  | 156 |  | if (line->str == NULL || line->ts == NULL) { | 
	|  | 157 |  | fatal(PMS_EXIT_MEMORY, "Out of memory"); | 
	|  | 158 |  | } | 
	| 159 | 159 |  |  | 
	| 160 |  | return line; |  | 
	|  | 160 |  | return line; | 
	| 161 | 161 | } | } | 
	| 162 | 162 |  |  | 
	| 163 | 163 | void free_logline(logline_t * line) { | void free_logline(logline_t * line) { | 
	| 164 |  | free(line->str); |  | 
	| 165 |  | free(line->ts); |  | 
	| 166 |  | free(line); |  | 
	|  | 164 |  | free(line->str); | 
	|  | 165 |  | free(line->ts); | 
	|  | 166 |  | free(line); | 
	| 167 | 167 | } | } | 
| File src/curses.c changed (mode: 100644) (index 98e8c51..3f74f83) | 
	| 1 |  | /* vi:set ts=4 sts=4 sw=4 noet: |  | 
	|  | 1 |  | /* vi:set ts=4 sts=4 sw=4 et: | 
	| 2 | 2 | * | * | 
	| 3 | 3 | * Practical Music Search | * Practical Music Search | 
	| 4 | 4 | * Copyright (c) 2006-2014 Kim Tore Jensen | * Copyright (c) 2006-2014 Kim Tore Jensen | 
|  | 
	| ... | ... | WINDOW * window_statusbar; | 
	| 27 | 27 | static pthread_mutex_t curses_mutex = PTHREAD_MUTEX_INITIALIZER; | static pthread_mutex_t curses_mutex = PTHREAD_MUTEX_INITIALIZER; | 
	| 28 | 28 |  |  | 
	| 29 | 29 | void curses_destroy_windows() { | void curses_destroy_windows() { | 
	| 30 |  | if (window_topbar) { |  | 
	| 31 |  | delwin(window_topbar); |  | 
	| 32 |  | } |  | 
	| 33 |  | if (window_main) { |  | 
	| 34 |  | delwin(window_main); |  | 
	| 35 |  | } |  | 
	| 36 |  | if (window_statusbar) { |  | 
	| 37 |  | delwin(window_statusbar); |  | 
	| 38 |  | } |  | 
	|  | 30 |  | if (window_topbar) { | 
	|  | 31 |  | delwin(window_topbar); | 
	|  | 32 |  | } | 
	|  | 33 |  | if (window_main) { | 
	|  | 34 |  | delwin(window_main); | 
	|  | 35 |  | } | 
	|  | 36 |  | if (window_statusbar) { | 
	|  | 37 |  | delwin(window_statusbar); | 
	|  | 38 |  | } | 
	| 39 | 39 | } | } | 
	| 40 | 40 |  |  | 
	| 41 | 41 | void curses_init_windows() { | void curses_init_windows() { | 
	| 42 |  | window_topbar = newwin(1, COLS, 0, 0); |  | 
	| 43 |  | window_main = newwin(LINES - 2, COLS, 1, 0); |  | 
	| 44 |  | window_statusbar = newwin(1, COLS, LINES - 1, 0); |  | 
	| 45 |  | scrollok(window_main, true); |  | 
	| 46 |  | scrollok(window_topbar, false); |  | 
	| 47 |  | scrollok(window_statusbar, false); |  | 
	| 48 |  | keypad(window_statusbar, true); |  | 
	|  | 42 |  | window_topbar = newwin(1, COLS, 0, 0); | 
	|  | 43 |  | window_main = newwin(LINES - 2, COLS, 1, 0); | 
	|  | 44 |  | window_statusbar = newwin(1, COLS, LINES - 1, 0); | 
	|  | 45 |  | scrollok(window_main, true); | 
	|  | 46 |  | scrollok(window_topbar, false); | 
	|  | 47 |  | scrollok(window_statusbar, false); | 
	|  | 48 |  | keypad(window_statusbar, true); | 
	| 49 | 49 | } | } | 
	| 50 | 50 |  |  | 
	| 51 | 51 | void curses_init() { | void curses_init() { | 
	| 52 |  | if ((initscr()) == NULL) { |  | 
	| 53 |  | fatal(PMS_EXIT_NCURSES, "Unable to start ncurses, exiting.\n"); |  | 
	| 54 |  | } |  | 
	|  | 52 |  | if ((initscr()) == NULL) { | 
	|  | 53 |  | fatal(PMS_EXIT_NCURSES, "Unable to start ncurses, exiting.\n"); | 
	|  | 54 |  | } | 
	| 55 | 55 |  |  | 
	| 56 |  | curses_init_windows(); |  | 
	|  | 56 |  | curses_init_windows(); | 
	| 57 | 57 |  |  | 
	| 58 |  | noecho(); |  | 
	| 59 |  | raw(); |  | 
	| 60 |  | curs_set(0); |  | 
	| 61 |  | halfdelay(10); |  | 
	|  | 58 |  | noecho(); | 
	|  | 59 |  | raw(); | 
	|  | 60 |  | curs_set(0); | 
	|  | 61 |  | halfdelay(10); | 
	| 62 | 62 |  |  | 
	| 63 | 63 | #ifdef HAVE_CURSES_COLOR | #ifdef HAVE_CURSES_COLOR | 
	| 64 |  | if (has_colors()) { |  | 
	| 65 |  | start_color(); |  | 
	| 66 |  | use_default_colors(); |  | 
	| 67 |  | } |  | 
	|  | 64 |  | if (has_colors()) { | 
	|  | 65 |  | start_color(); | 
	|  | 66 |  | use_default_colors(); | 
	|  | 67 |  | } | 
	| 68 | 68 | #endif | #endif | 
	| 69 | 69 |  |  | 
	| 70 |  | wclear(window_topbar); |  | 
	| 71 |  | wclear(window_main); |  | 
	| 72 |  | wclear(window_statusbar); |  | 
	| 73 |  | wrefresh(window_topbar); |  | 
	| 74 |  | wrefresh(window_main); |  | 
	| 75 |  | wrefresh(window_statusbar); |  | 
	|  | 70 |  | wclear(window_topbar); | 
	|  | 71 |  | wclear(window_main); | 
	|  | 72 |  | wclear(window_statusbar); | 
	|  | 73 |  | wrefresh(window_topbar); | 
	|  | 74 |  | wrefresh(window_main); | 
	|  | 75 |  | wrefresh(window_statusbar); | 
	| 76 | 76 | } | } | 
	| 77 | 77 |  |  | 
	| 78 | 78 | void curses_shutdown() { | void curses_shutdown() { | 
	| 79 |  | endwin(); |  | 
	|  | 79 |  | endwin(); | 
	| 80 | 80 | } | } | 
	| 81 | 81 |  |  | 
	| 82 | 82 | int curses_get_input() { | int curses_get_input() { | 
	| 83 |  | return wgetch(window_statusbar); |  | 
	|  | 83 |  | return wgetch(window_statusbar); | 
	| 84 | 84 | } | } | 
	| 85 | 85 |  |  | 
	| 86 | 86 | void pms_curses_lock() { | void pms_curses_lock() { | 
	| 87 |  | pthread_mutex_lock(&curses_mutex); |  | 
	|  | 87 |  | pthread_mutex_lock(&curses_mutex); | 
	| 88 | 88 | } | } | 
	| 89 | 89 |  |  | 
	| 90 | 90 | void pms_curses_unlock() { | void pms_curses_unlock() { | 
	| 91 |  | pthread_mutex_unlock(&curses_mutex); |  | 
	|  | 91 |  | pthread_mutex_unlock(&curses_mutex); | 
	| 92 | 92 | } | } | 
| File src/input.c changed (mode: 100644) (index f8a99aa..136cc5d) | 
	| 1 |  | /* vi:set ts=4 sts=4 sw=4 noet: |  | 
	|  | 1 |  | /* vi:set ts=4 sts=4 sw=4 et: | 
	| 2 | 2 | * | * | 
	| 3 | 3 | * Practical Music Search | * Practical Music Search | 
	| 4 | 4 | * Copyright (c) 2006-2014 Kim Tore Jensen | * Copyright (c) 2006-2014 Kim Tore Jensen | 
|  | 
	| ... | ... | static int input_char_get_int(int ch) { | 
	| 31 | 31 | } | } | 
	| 32 | 32 |  |  | 
	| 33 | 33 | static int input_char_get_movement(int ch) { | static int input_char_get_movement(int ch) { | 
	| 34 |  | if (ch == KEY_UP || ch == 'k') { |  | 
	|  | 34 |  | if (ch == KEY_UP || ch == 'k') { | 
	| 35 | 35 | return INPUT_MOVEMENT_UP; | return INPUT_MOVEMENT_UP; | 
	| 36 |  | } else if (ch == KEY_DOWN || ch == 'j') { |  | 
	|  | 36 |  | } else if (ch == KEY_DOWN || ch == 'j') { | 
	| 37 | 37 | return INPUT_MOVEMENT_DOWN; | return INPUT_MOVEMENT_DOWN; | 
	| 38 |  | } else if (ch == 'G') { |  | 
	|  | 38 |  | } else if (ch == 'G') { | 
	| 39 | 39 | return INPUT_MOVEMENT_END; | return INPUT_MOVEMENT_END; | 
	| 40 | 40 | } | } | 
	| 41 | 41 | return INPUT_MOVEMENT_NONE; | return INPUT_MOVEMENT_NONE; | 
	| 42 | 42 | } | } | 
	| 43 | 43 |  |  | 
	| 44 | 44 | static int input_char_get_action(int ch) { | static int input_char_get_action(int ch) { | 
	| 45 |  | if (ch == KEY_UP || ch == 'k') { |  | 
	|  | 45 |  | if (ch == KEY_UP || ch == 'k') { | 
	| 46 | 46 | return INPUT_ACTION_GO; | return INPUT_ACTION_GO; | 
	| 47 |  | } else if (ch == KEY_DOWN || ch == 'j') { |  | 
	|  | 47 |  | } else if (ch == KEY_DOWN || ch == 'j') { | 
	| 48 | 48 | return INPUT_ACTION_GO; | return INPUT_ACTION_GO; | 
	| 49 | 49 | } else if (ch == 'q') { | } else if (ch == 'q') { | 
	| 50 | 50 | return INPUT_ACTION_QUIT; | return INPUT_ACTION_QUIT; | 
|  | 
	| ... | ... | command_t * input_get() { | 
	| 119 | 119 | int input_handle(command_t * command) { | int input_handle(command_t * command) { | 
	| 120 | 120 | console("input_handle(): multiplier=%d, movement=%d, action=%d", command->multiplier, command->movement, command->action); | console("input_handle(): multiplier=%d, movement=%d, action=%d", command->multiplier, command->movement, command->action); | 
	| 121 | 121 | if (command->action == INPUT_ACTION_QUIT) { | if (command->action == INPUT_ACTION_QUIT) { | 
	| 122 |  | shutdown(); |  | 
	|  | 122 |  | shutdown(); | 
	| 123 | 123 | return 0; | return 0; | 
	| 124 |  | } else if (command->action == INPUT_ACTION_GO) { |  | 
	|  | 124 |  | } else if (command->action == INPUT_ACTION_GO) { | 
	| 125 | 125 | if (command->movement == INPUT_MOVEMENT_UP) { | if (command->movement == INPUT_MOVEMENT_UP) { | 
	| 126 | 126 | console_scroll(-command->multiplier); | console_scroll(-command->multiplier); | 
	| 127 | 127 | } else if (command->movement == INPUT_MOVEMENT_DOWN) { | } else if (command->movement == INPUT_MOVEMENT_DOWN) { | 
|  | 
	| ... | ... | int input_handle(command_t * command) { | 
	| 133 | 133 | console_scroll_to(command->multiplier-1); // convert 1-indexed to 0-indexed | console_scroll_to(command->multiplier-1); // convert 1-indexed to 0-indexed | 
	| 134 | 134 | return 0; | return 0; | 
	| 135 | 135 | } | } | 
	| 136 |  | } else { |  | 
	|  | 136 |  | } else { | 
	| 137 | 137 | return 0; | return 0; | 
	| 138 | 138 | } | } | 
	| 139 | 139 | return 1; | return 1; | 
| File src/pms.c changed (mode: 100644) (index c4c93bd..646d9a7) | 
	| 1 |  | /* vi:set ts=4 sts=4 sw=4 noet: |  | 
	|  | 1 |  | /* vi:set ts=4 sts=4 sw=4 et: | 
	| 2 | 2 | * | * | 
	| 3 | 3 | * Practical Music Search | * Practical Music Search | 
	| 4 | 4 | * Copyright (c) 2006-2014 Kim Tore Jensen | * Copyright (c) 2006-2014 Kim Tore Jensen | 
|  | 
	| ... | ... | static pthread_mutex_t status_mutex = PTHREAD_MUTEX_INITIALIZER; | 
	| 34 | 34 |  |  | 
	| 35 | 35 | void reset_options() { | void reset_options() { | 
	| 36 | 36 |  |  | 
	| 37 |  | if (options == NULL) { |  | 
	| 38 |  | options = malloc(sizeof(struct options_t)); |  | 
	| 39 |  | } |  | 
	|  | 37 |  | if (options == NULL) { | 
	|  | 38 |  | options = malloc(sizeof(struct options_t)); | 
	|  | 39 |  | } | 
	| 40 | 40 |  |  | 
	| 41 |  | if (options == NULL) { |  | 
	| 42 |  | perror("out of memory\n"); |  | 
	| 43 |  | exit(PMS_EXIT_MEMORY); |  | 
	| 44 |  | } |  | 
	|  | 41 |  | if (options == NULL) { | 
	|  | 42 |  | perror("out of memory\n"); | 
	|  | 43 |  | exit(PMS_EXIT_MEMORY); | 
	|  | 44 |  | } | 
	| 45 | 45 |  |  | 
	| 46 |  | options->server = "localhost"; |  | 
	| 47 |  | options->port = 0; |  | 
	| 48 |  | options->timeout = 2000; |  | 
	| 49 |  | options->console_size = 1024; |  | 
	|  | 46 |  | options->server = "localhost"; | 
	|  | 47 |  | options->port = 0; | 
	|  | 48 |  | options->timeout = 2000; | 
	|  | 49 |  | options->console_size = 1024; | 
	| 50 | 50 |  |  | 
	| 51 | 51 | } | } | 
	| 52 | 52 |  |  | 
	| 53 | 53 | void fatal(int exitcode, const char * format, ...) { | void fatal(int exitcode, const char * format, ...) { | 
	| 54 |  | va_list ap; |  | 
	| 55 |  | curses_shutdown(); |  | 
	| 56 |  | va_start(ap, format); |  | 
	| 57 |  | vprintf(format, ap); |  | 
	| 58 |  | va_end(ap); |  | 
	| 59 |  | exit(exitcode); |  | 
	|  | 54 |  | va_list ap; | 
	|  | 55 |  | curses_shutdown(); | 
	|  | 56 |  | va_start(ap, format); | 
	|  | 57 |  | vprintf(format, ap); | 
	|  | 58 |  | va_end(ap); | 
	|  | 59 |  | exit(exitcode); | 
	| 60 | 60 | } | } | 
	| 61 | 61 |  |  | 
	| 62 | 62 | void shutdown() { | void shutdown() { | 
	| 63 |  | console("Shutting down."); |  | 
	| 64 |  | pms_state->running = 0; |  | 
	|  | 63 |  | console("Shutting down."); | 
	|  | 64 |  | pms_state->running = 0; | 
	| 65 | 65 | } | } | 
	| 66 | 66 |  |  | 
	| 67 | 67 | static struct mpd_connection * pms_mpd_connect() { | static struct mpd_connection * pms_mpd_connect() { | 
	| 68 | 68 |  |  | 
	| 69 |  | enum mpd_error status; |  | 
	| 70 |  | const char * error_msg; |  | 
	| 71 |  | struct mpd_connection * connection; |  | 
	| 72 |  |  |  | 
	| 73 |  | console("Connecting to %s...", options->server); |  | 
	| 74 |  |  |  | 
	| 75 |  | connection = mpd_connection_new(options->server, options->port, options->timeout); |  | 
	| 76 |  | if (connection == NULL) { |  | 
	| 77 |  | fatal(PMS_EXIT_MEMORY, "mpd connect: out of memory\n"); |  | 
	| 78 |  | } |  | 
	| 79 |  |  |  | 
	| 80 |  | status = mpd_connection_get_error(connection); |  | 
	| 81 |  | if (status == MPD_ERROR_SUCCESS) { |  | 
	| 82 |  | console("Connected to %s", options->server); |  | 
	| 83 |  | } else { |  | 
	| 84 |  | error_msg = mpd_connection_get_error_message(connection); |  | 
	| 85 |  | console("Error connecting to %s: error %d: %s", options->server, status, error_msg); |  | 
	| 86 |  | mpd_connection_free(connection); |  | 
	| 87 |  | connection = NULL; |  | 
	| 88 |  | } |  | 
	| 89 |  |  |  | 
	| 90 |  | return connection; |  | 
	|  | 69 |  | enum mpd_error status; | 
	|  | 70 |  | const char * error_msg; | 
	|  | 71 |  | struct mpd_connection * connection; | 
	|  | 72 |  |  | 
	|  | 73 |  | console("Connecting to %s...", options->server); | 
	|  | 74 |  |  | 
	|  | 75 |  | connection = mpd_connection_new(options->server, options->port, options->timeout); | 
	|  | 76 |  | if (connection == NULL) { | 
	|  | 77 |  | fatal(PMS_EXIT_MEMORY, "mpd connect: out of memory\n"); | 
	|  | 78 |  | } | 
	|  | 79 |  |  | 
	|  | 80 |  | status = mpd_connection_get_error(connection); | 
	|  | 81 |  | if (status == MPD_ERROR_SUCCESS) { | 
	|  | 82 |  | console("Connected to %s", options->server); | 
	|  | 83 |  | } else { | 
	|  | 84 |  | error_msg = mpd_connection_get_error_message(connection); | 
	|  | 85 |  | console("Error connecting to %s: error %d: %s", options->server, status, error_msg); | 
	|  | 86 |  | mpd_connection_free(connection); | 
	|  | 87 |  | connection = NULL; | 
	|  | 88 |  | } | 
	|  | 89 |  |  | 
	|  | 90 |  | return connection; | 
	| 91 | 91 | } | } | 
	| 92 | 92 |  |  | 
	| 93 | 93 | static void pms_get_mpd_state(struct mpd_connection * connection) { | static void pms_get_mpd_state(struct mpd_connection * connection) { | 
	| 94 | 94 |  |  | 
	| 95 |  | pms_status_lock(); |  | 
	| 96 |  | if (pms_state->status) { |  | 
	| 97 |  | mpd_status_free(pms_state->status); |  | 
	| 98 |  | } |  | 
	| 99 |  | pms_state->status = mpd_run_status(connection); |  | 
	| 100 |  | pms_status_unlock(); |  | 
	|  | 95 |  | pms_status_lock(); | 
	|  | 96 |  | if (pms_state->status) { | 
	|  | 97 |  | mpd_status_free(pms_state->status); | 
	|  | 98 |  | } | 
	|  | 99 |  | pms_state->status = mpd_run_status(connection); | 
	|  | 100 |  | pms_status_unlock(); | 
	| 101 | 101 |  |  | 
	| 102 | 102 | } | } | 
	| 103 | 103 |  |  | 
	| 104 | 104 | static void pms_handle_mpd_idle_update(struct mpd_connection * connection, enum mpd_idle flags) { | static void pms_handle_mpd_idle_update(struct mpd_connection * connection, enum mpd_idle flags) { | 
	| 105 | 105 |  |  | 
	| 106 |  | console("pms_handle_mpd_idle_update %d", flags); |  | 
	| 107 |  |  |  | 
	| 108 |  | if (flags & MPD_IDLE_DATABASE) { |  | 
	| 109 |  | console("Database has been updated."); |  | 
	| 110 |  | } |  | 
	| 111 |  | if (flags & MPD_IDLE_STORED_PLAYLIST) { |  | 
	| 112 |  | console("Stored playlists have been updated."); |  | 
	| 113 |  | } |  | 
	| 114 |  | if (flags & MPD_IDLE_QUEUE) { |  | 
	| 115 |  | console("The queue has been updated."); |  | 
	| 116 |  | } |  | 
	| 117 |  | if (flags & MPD_IDLE_PLAYER) { |  | 
	| 118 |  | console("Player state has changed."); |  | 
	| 119 |  | } |  | 
	| 120 |  | if (flags & MPD_IDLE_MIXER) { |  | 
	| 121 |  | console("Mixer parameters have changed."); |  | 
	| 122 |  | } |  | 
	| 123 |  | if (flags & MPD_IDLE_OUTPUT) { |  | 
	| 124 |  | console("Outputs have changed."); |  | 
	| 125 |  | } |  | 
	| 126 |  | if (flags & MPD_IDLE_OPTIONS) { |  | 
	| 127 |  | console("Options have changed."); |  | 
	| 128 |  | } |  | 
	| 129 |  | if (flags & MPD_IDLE_UPDATE) { |  | 
	| 130 |  | console("Database update has started or finished."); |  | 
	| 131 |  | } |  | 
	| 132 |  |  |  | 
	| 133 |  | if (flags & (MPD_IDLE_QUEUE | MPD_IDLE_PLAYER | MPD_IDLE_MIXER | MPD_IDLE_OPTIONS)) { |  | 
	| 134 |  | pms_get_mpd_state(connection); |  | 
	| 135 |  | } |  | 
	| 136 |  |  |  | 
	| 137 |  | topbar_draw(); |  | 
	|  | 106 |  | console("pms_handle_mpd_idle_update %d", flags); | 
	|  | 107 |  |  | 
	|  | 108 |  | if (flags & MPD_IDLE_DATABASE) { | 
	|  | 109 |  | console("Database has been updated."); | 
	|  | 110 |  | } | 
	|  | 111 |  | if (flags & MPD_IDLE_STORED_PLAYLIST) { | 
	|  | 112 |  | console("Stored playlists have been updated."); | 
	|  | 113 |  | } | 
	|  | 114 |  | if (flags & MPD_IDLE_QUEUE) { | 
	|  | 115 |  | console("The queue has been updated."); | 
	|  | 116 |  | } | 
	|  | 117 |  | if (flags & MPD_IDLE_PLAYER) { | 
	|  | 118 |  | console("Player state has changed."); | 
	|  | 119 |  | } | 
	|  | 120 |  | if (flags & MPD_IDLE_MIXER) { | 
	|  | 121 |  | console("Mixer parameters have changed."); | 
	|  | 122 |  | } | 
	|  | 123 |  | if (flags & MPD_IDLE_OUTPUT) { | 
	|  | 124 |  | console("Outputs have changed."); | 
	|  | 125 |  | } | 
	|  | 126 |  | if (flags & MPD_IDLE_OPTIONS) { | 
	|  | 127 |  | console("Options have changed."); | 
	|  | 128 |  | } | 
	|  | 129 |  | if (flags & MPD_IDLE_UPDATE) { | 
	|  | 130 |  | console("Database update has started or finished."); | 
	|  | 131 |  | } | 
	|  | 132 |  |  | 
	|  | 133 |  | if (flags & (MPD_IDLE_QUEUE | MPD_IDLE_PLAYER | MPD_IDLE_MIXER | MPD_IDLE_OPTIONS)) { | 
	|  | 134 |  | pms_get_mpd_state(connection); | 
	|  | 135 |  | } | 
	|  | 136 |  |  | 
	|  | 137 |  | topbar_draw(); | 
	| 138 | 138 |  |  | 
	| 139 | 139 | } | } | 
	| 140 | 140 |  |  | 
	| 141 | 141 | void signal_resize(int signal) { | void signal_resize(int signal) { | 
	| 142 |  | console("Resized to %d x %d", LINES, COLS); |  | 
	| 143 |  | pms_curses_lock(); |  | 
	| 144 |  | curses_destroy_windows(); |  | 
	| 145 |  | curses_init_windows(); |  | 
	| 146 |  | pms_curses_unlock(); |  | 
	|  | 142 |  | console("Resized to %d x %d", LINES, COLS); | 
	|  | 143 |  | pms_curses_lock(); | 
	|  | 144 |  | curses_destroy_windows(); | 
	|  | 145 |  | curses_init_windows(); | 
	|  | 146 |  | pms_curses_unlock(); | 
	| 147 | 147 | } | } | 
	| 148 | 148 |  |  | 
	| 149 | 149 | void signal_kill(int signal) { | void signal_kill(int signal) { | 
	| 150 |  | fatal(PMS_EXIT_KILLED, "Killed by signal %d\n", signal); |  | 
	|  | 150 |  | fatal(PMS_EXIT_KILLED, "Killed by signal %d\n", signal); | 
	| 151 | 151 | } | } | 
	| 152 | 152 |  |  | 
	| 153 | 153 | static void signal_init() { | static void signal_init() { | 
	| 154 |  | if (signal(SIGWINCH, signal_resize) == SIG_ERR) { |  | 
	| 155 |  | console("Error in signal_init(): window resizing will not work!"); |  | 
	| 156 |  | } |  | 
	| 157 |  | signal(SIGTERM, signal_kill); |  | 
	|  | 154 |  | if (signal(SIGWINCH, signal_resize) == SIG_ERR) { | 
	|  | 155 |  | console("Error in signal_init(): window resizing will not work!"); | 
	|  | 156 |  | } | 
	|  | 157 |  | signal(SIGTERM, signal_kill); | 
	| 158 | 158 | } | } | 
	| 159 | 159 |  |  | 
	| 160 | 160 | int pms_get_pending_input_flags(struct mpd_connection * connection) { | int pms_get_pending_input_flags(struct mpd_connection * connection) { | 
	| 161 |  | struct timeval tv; |  | 
	| 162 |  | int mpd_fd = 0; |  | 
	| 163 |  | int retval; |  | 
	| 164 |  | int flags = 0; |  | 
	| 165 |  | fd_set fds; |  | 
	| 166 |  |  |  | 
	| 167 |  | tv.tv_sec = 1; |  | 
	| 168 |  | tv.tv_usec = 0; |  | 
	| 169 |  |  |  | 
	| 170 |  | if (connection) { |  | 
	| 171 |  | mpd_fd = mpd_connection_get_fd(connection); |  | 
	| 172 |  | } |  | 
	| 173 |  |  |  | 
	| 174 |  | FD_ZERO(&fds); |  | 
	| 175 |  | FD_SET(STDIN_FILENO, &fds); |  | 
	| 176 |  | FD_SET(mpd_fd, &fds); |  | 
	| 177 |  |  |  | 
	| 178 |  | retval = select(mpd_fd+1, &fds, NULL, NULL, &tv); |  | 
	| 179 |  |  |  | 
	| 180 |  | if (retval == -1) { |  | 
	| 181 |  | console("Error %d in select(): %s", errno, strerror(errno)); |  | 
	| 182 |  |  |  | 
	| 183 |  | } else if (retval > 0) { |  | 
	| 184 |  | if (mpd_fd != 0 && FD_ISSET(mpd_fd, &fds)) { |  | 
	| 185 |  | flags |= PMS_HAS_INPUT_MPD; |  | 
	| 186 |  | } |  | 
	| 187 |  | if (FD_ISSET(STDIN_FILENO, &fds)) { |  | 
	| 188 |  | flags |= PMS_HAS_INPUT_STDIN; |  | 
	| 189 |  | } |  | 
	| 190 |  | } |  | 
	| 191 |  |  |  | 
	| 192 |  | return flags; |  | 
	|  | 161 |  | struct timeval tv; | 
	|  | 162 |  | int mpd_fd = 0; | 
	|  | 163 |  | int retval; | 
	|  | 164 |  | int flags = 0; | 
	|  | 165 |  | fd_set fds; | 
	|  | 166 |  |  | 
	|  | 167 |  | tv.tv_sec = 1; | 
	|  | 168 |  | tv.tv_usec = 0; | 
	|  | 169 |  |  | 
	|  | 170 |  | if (connection) { | 
	|  | 171 |  | mpd_fd = mpd_connection_get_fd(connection); | 
	|  | 172 |  | } | 
	|  | 173 |  |  | 
	|  | 174 |  | FD_ZERO(&fds); | 
	|  | 175 |  | FD_SET(STDIN_FILENO, &fds); | 
	|  | 176 |  | FD_SET(mpd_fd, &fds); | 
	|  | 177 |  |  | 
	|  | 178 |  | retval = select(mpd_fd+1, &fds, NULL, NULL, &tv); | 
	|  | 179 |  |  | 
	|  | 180 |  | if (retval == -1) { | 
	|  | 181 |  | console("Error %d in select(): %s", errno, strerror(errno)); | 
	|  | 182 |  |  | 
	|  | 183 |  | } else if (retval > 0) { | 
	|  | 184 |  | if (mpd_fd != 0 && FD_ISSET(mpd_fd, &fds)) { | 
	|  | 185 |  | flags |= PMS_HAS_INPUT_MPD; | 
	|  | 186 |  | } | 
	|  | 187 |  | if (FD_ISSET(STDIN_FILENO, &fds)) { | 
	|  | 188 |  | flags |= PMS_HAS_INPUT_STDIN; | 
	|  | 189 |  | } | 
	|  | 190 |  | } | 
	|  | 191 |  |  | 
	|  | 192 |  | return flags; | 
	| 193 | 193 | } | } | 
	| 194 | 194 |  |  | 
	| 195 | 195 | int main(int argc, char** argv) { | int main(int argc, char** argv) { | 
	| 196 | 196 |  |  | 
	| 197 |  | command_t * command = NULL; |  | 
	| 198 |  | struct mpd_connection * connection = NULL; |  | 
	| 199 |  | bool is_idle = false; |  | 
	| 200 |  | enum mpd_idle flags = -1; |  | 
	| 201 |  | int input_flags = 0; |  | 
	| 202 |  |  |  | 
	| 203 |  | reset_options(); |  | 
	| 204 |  | pms_state = malloc(sizeof(struct pms_state_t)); |  | 
	| 205 |  | memset(pms_state, 0, sizeof(struct pms_state_t)); |  | 
	| 206 |  | pms_state->running = true; |  | 
	| 207 |  |  |  | 
	| 208 |  | curses_init(); |  | 
	| 209 |  | console_init(options->console_size); |  | 
	| 210 |  | signal_init(); |  | 
	| 211 |  | input_reset(); |  | 
	| 212 |  | console("%s %s (c) 2006-2014 Kim Tore Jensen <%s>", PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_BUGREPORT); |  | 
	| 213 |  |  |  | 
	| 214 |  | while(pms_state->running) { |  | 
	| 215 |  |  |  | 
	| 216 |  | if (!connection) { |  | 
	| 217 |  | if (connection = pms_mpd_connect()) { |  | 
	| 218 |  | pms_handle_mpd_idle_update(connection, -1); |  | 
	| 219 |  | } |  | 
	| 220 |  | } |  | 
	| 221 |  |  |  | 
	| 222 |  | if (connection && !is_idle) { |  | 
	| 223 |  | mpd_send_idle(connection); |  | 
	| 224 |  | is_idle = true; |  | 
	| 225 |  | } |  | 
	| 226 |  |  |  | 
	| 227 |  | input_flags = pms_get_pending_input_flags(connection); |  | 
	| 228 |  |  |  | 
	| 229 |  | if (input_flags & PMS_HAS_INPUT_MPD) { |  | 
	| 230 |  | flags = mpd_recv_idle(connection, true); |  | 
	| 231 |  | is_idle = false; |  | 
	| 232 |  | pms_handle_mpd_idle_update(connection, flags); |  | 
	| 233 |  | } |  | 
	| 234 |  |  |  | 
	| 235 |  | if (input_flags & PMS_HAS_INPUT_STDIN) { |  | 
	| 236 |  | if ((command = input_get()) != NULL) { |  | 
	| 237 |  | while(command->multiplier > 0 && input_handle(command)) { |  | 
	| 238 |  | --command->multiplier; |  | 
	| 239 |  | } |  | 
	| 240 |  | input_reset(); |  | 
	| 241 |  | } |  | 
	| 242 |  | } |  | 
	| 243 |  |  |  | 
	| 244 |  | } |  | 
	| 245 |  |  |  | 
	| 246 |  | curses_shutdown(); |  | 
	| 247 |  |  |  | 
	| 248 |  | return PMS_EXIT_SUCCESS; |  | 
	|  | 197 |  | command_t * command = NULL; | 
	|  | 198 |  | struct mpd_connection * connection = NULL; | 
	|  | 199 |  | bool is_idle = false; | 
	|  | 200 |  | enum mpd_idle flags = -1; | 
	|  | 201 |  | int input_flags = 0; | 
	|  | 202 |  |  | 
	|  | 203 |  | reset_options(); | 
	|  | 204 |  | pms_state = malloc(sizeof(struct pms_state_t)); | 
	|  | 205 |  | memset(pms_state, 0, sizeof(struct pms_state_t)); | 
	|  | 206 |  | pms_state->running = true; | 
	|  | 207 |  |  | 
	|  | 208 |  | curses_init(); | 
	|  | 209 |  | console_init(options->console_size); | 
	|  | 210 |  | signal_init(); | 
	|  | 211 |  | input_reset(); | 
	|  | 212 |  | console("%s %s (c) 2006-2014 Kim Tore Jensen <%s>", PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_BUGREPORT); | 
	|  | 213 |  |  | 
	|  | 214 |  | while(pms_state->running) { | 
	|  | 215 |  |  | 
	|  | 216 |  | if (!connection) { | 
	|  | 217 |  | if (connection = pms_mpd_connect()) { | 
	|  | 218 |  | pms_handle_mpd_idle_update(connection, -1); | 
	|  | 219 |  | } | 
	|  | 220 |  | } | 
	|  | 221 |  |  | 
	|  | 222 |  | if (connection && !is_idle) { | 
	|  | 223 |  | mpd_send_idle(connection); | 
	|  | 224 |  | is_idle = true; | 
	|  | 225 |  | } | 
	|  | 226 |  |  | 
	|  | 227 |  | input_flags = pms_get_pending_input_flags(connection); | 
	|  | 228 |  |  | 
	|  | 229 |  | if (input_flags & PMS_HAS_INPUT_MPD) { | 
	|  | 230 |  | flags = mpd_recv_idle(connection, true); | 
	|  | 231 |  | is_idle = false; | 
	|  | 232 |  | pms_handle_mpd_idle_update(connection, flags); | 
	|  | 233 |  | } | 
	|  | 234 |  |  | 
	|  | 235 |  | if (input_flags & PMS_HAS_INPUT_STDIN) { | 
	|  | 236 |  | if ((command = input_get()) != NULL) { | 
	|  | 237 |  | while(command->multiplier > 0 && input_handle(command)) { | 
	|  | 238 |  | --command->multiplier; | 
	|  | 239 |  | } | 
	|  | 240 |  | input_reset(); | 
	|  | 241 |  | } | 
	|  | 242 |  | } | 
	|  | 243 |  |  | 
	|  | 244 |  | } | 
	|  | 245 |  |  | 
	|  | 246 |  | curses_shutdown(); | 
	|  | 247 |  |  | 
	|  | 248 |  | return PMS_EXIT_SUCCESS; | 
	| 249 | 249 | } | } | 
	| 250 | 250 |  |  | 
	| 251 | 251 | void pms_status_lock() { | void pms_status_lock() { | 
	| 252 |  | pthread_mutex_lock(&status_mutex); |  | 
	|  | 252 |  | pthread_mutex_lock(&status_mutex); | 
	| 253 | 253 | } | } | 
	| 254 | 254 |  |  | 
	| 255 | 255 | void pms_status_unlock() { | void pms_status_unlock() { | 
	| 256 |  | pthread_mutex_unlock(&status_mutex); |  | 
	|  | 256 |  | pthread_mutex_unlock(&status_mutex); | 
	| 257 | 257 | } | } |