Mercurial > pidgin
view console/libgnt/gntmain.c @ 14762:6dfb8f540cae
[gaim-migrate @ 17519]
b4
committer: Tailor Script <tailor@pidgin.im>
author | Nathan Walp <nwalp@pidgin.im> |
---|---|
date | Wed, 18 Oct 2006 06:02:39 +0000 |
parents | 3d1be4577fa9 |
children | 70623f0d5cdc |
line wrap: on
line source
#define _GNU_SOURCE #if defined(__APPLE__) #define _XOPEN_SOURCE_EXTENDED #endif #include "config.h" #include <gmodule.h> #include "gnt.h" #include "gntbox.h" #include "gntcolors.h" #include "gntkeys.h" #include "gntmenu.h" #include "gntstyle.h" #include "gnttree.h" #include "gntutils.h" #include "gntwm.h" #include <panel.h> #include <stdio.h> #include <stdlib.h> #include <locale.h> #include <unistd.h> #include <signal.h> #include <string.h> #include <ctype.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> /** * Notes: Interesting functions to look at: * scr_dump, scr_init, scr_restore: for workspaces * * Need to wattrset for colors to use with PDCurses. */ /** * There can be at most one menu at a time on the screen. * If there is a menu being displayed, then all the keystrokes will be sent to * the menu until it is closed, either when the user activates a menuitem, or * presses Escape to cancel the menu. */ static GntMenu *menu; static int lock_focus_list; static GList *focus_list; static GList *ordered; static int X_MIN; static int X_MAX; static int Y_MIN; static int Y_MAX; static gboolean ascii_only; static gboolean mouse_enabled; /** * 'event_stack' will be set to TRUE when a user-event, ie. a mouse-click * or a key-press is being processed. This variable will be used to determine * whether to give focus to a new window. */ static gboolean event_stack; static GMainLoop *loop; static struct { GntWidget *window; GntWidget *tree; } _list, *window_list, *action_list; typedef struct { GntWidget *me; PANEL *panel; } GntNode; typedef enum { GNT_KP_MODE_NORMAL, GNT_KP_MODE_RESIZE, GNT_KP_MODE_MOVE, GNT_KP_MODE_MENU, GNT_KP_MODE_WINDOW_LIST } GntKeyPressMode; static GHashTable *nodes; static void free_node(gpointer data); static void draw_taskbar(gboolean reposition); static void bring_on_top(GntWidget *widget); static gboolean refresh_screen(); static const GList *list_all_windows(); static void show_actions_list(); static GntWM wm = { NULL, /* new_window */ NULL, /* close_window */ NULL, /* window_resize_confirm */ NULL, /* window_resized */ NULL, /* window_move_confirm */ NULL, /* window_moved */ NULL, /* window_update */ NULL, /* key_pressed */ NULL, /* mouse clicked */ bring_on_top, /* give_focus */ NULL, /* uninit */ list_all_windows, /* window_list */ }; static const GList *list_all_windows() { return focus_list; } static GList * g_list_bring_to_front(GList *list, gpointer data) { list = g_list_remove(list, data); list = g_list_prepend(list, data); return list; } static gboolean update_screen(gpointer null) { if (menu) { GntMenu *top = menu; while (top) { GntNode *node = g_hash_table_lookup(nodes, top); if (node) top_panel(node->panel); top = top->submenu; } } update_panels(); doupdate(); return TRUE; } void gnt_screen_take_focus(GntWidget *widget) { GntWidget *w = NULL; if (lock_focus_list) return; if (g_list_find(focus_list, widget)) return; if (ordered) w = ordered->data; focus_list = g_list_append(focus_list, widget); if (event_stack) { ordered = g_list_prepend(ordered, widget); g_object_set_data(G_OBJECT(widget), "give_focus", GINT_TO_POINTER(event_stack)); } else ordered = g_list_append(ordered, widget); gnt_widget_set_focus(widget, TRUE); if (w) gnt_widget_set_focus(w, FALSE); draw_taskbar(FALSE); } void gnt_screen_remove_widget(GntWidget *widget) { int pos = g_list_index(focus_list, widget); if (lock_focus_list) return; if (pos == -1) return; focus_list = g_list_remove(focus_list, widget); ordered = g_list_remove(ordered, widget); if (ordered) { wm.give_focus(ordered->data); } draw_taskbar(FALSE); } static void bring_on_top(GntWidget *widget) { GntNode *node = g_hash_table_lookup(nodes, widget); if (!node) return; if (ordered->data != widget) { GntWidget *w = ordered->data; ordered = g_list_bring_to_front(ordered, widget); gnt_widget_set_focus(w, FALSE); } gnt_widget_set_focus(widget, TRUE); gnt_widget_draw(widget); top_panel(node->panel); if (_list.window) { GntNode *nd = g_hash_table_lookup(nodes, _list.window); top_panel(nd->panel); } update_screen(NULL); draw_taskbar(FALSE); } static void update_window_in_list(GntWidget *wid) { GntTextFormatFlags flag = 0; if (window_list == NULL) return; if (wid == ordered->data) flag |= GNT_TEXT_FLAG_DIM; else if (GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_URGENT)) flag |= GNT_TEXT_FLAG_BOLD; gnt_tree_set_row_flags(GNT_TREE(window_list->tree), wid, flag); } static void draw_taskbar(gboolean reposition) { static WINDOW *taskbar = NULL; GList *iter; int n, width = 0; int i; if (taskbar == NULL) { taskbar = newwin(1, getmaxx(stdscr), getmaxy(stdscr) - 1, 0); } else if (reposition) { mvwin(taskbar, Y_MAX, 0); } wbkgdset(taskbar, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); werase(taskbar); n = g_list_length(focus_list); if (n) width = getmaxx(stdscr) / n; for (i = 0, iter = focus_list; iter; iter = iter->next, i++) { GntWidget *w = iter->data; int color; const char *title; if (w == ordered->data) { /* This is the current window in focus */ color = GNT_COLOR_TITLE; GNT_WIDGET_UNSET_FLAGS(w, GNT_WIDGET_URGENT); if (wm.window_update) { GntNode *node = g_hash_table_lookup(nodes, w); wm.window_update(node ? node->panel : NULL, w); } } else if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_URGENT)) { /* This is a window with the URGENT hint set */ color = GNT_COLOR_URGENT; } else { color = GNT_COLOR_NORMAL; } wbkgdset(taskbar, '\0' | COLOR_PAIR(color)); mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), width); title = GNT_BOX(w)->title; mvwprintw(taskbar, 0, width * i, "%s", title ? title : "<gnt>"); if (i) mvwaddch(taskbar, 0, width *i - 1, ACS_VLINE | A_STANDOUT | COLOR_PAIR(GNT_COLOR_NORMAL)); update_window_in_list(w); } wrefresh(taskbar); } static void switch_window(int direction) { GntWidget *w = NULL, *wid = NULL; int pos; if (!ordered || !ordered->next) return; w = ordered->data; pos = g_list_index(focus_list, w); pos += direction; if (pos < 0) wid = g_list_last(focus_list)->data; else if (pos >= g_list_length(focus_list)) wid = focus_list->data; else if (pos >= 0) wid = g_list_nth_data(focus_list, pos); ordered = g_list_bring_to_front(ordered, wid); wm.give_focus(ordered->data); if (w != wid) { gnt_widget_set_focus(w, FALSE); } } static void switch_window_n(int n) { GntWidget *w = NULL; GList *l; if (!ordered) return; w = ordered->data; if ((l = g_list_nth(focus_list, n)) != NULL) { ordered = g_list_bring_to_front(ordered, l->data); wm.give_focus(ordered->data); } if (l && w != l->data) { gnt_widget_set_focus(w, FALSE); } } static void window_list_activate(GntTree *tree, gpointer null) { GntWidget *widget = gnt_tree_get_selection_data(GNT_TREE(tree)); GntWidget *old = NULL; if (!ordered || !widget) return; old = ordered->data; ordered = g_list_bring_to_front(ordered, widget); wm.give_focus(widget); if (old != widget) { gnt_widget_set_focus(old, FALSE); } } static void setup__list() { GntWidget *tree, *win; win = _list.window = gnt_box_new(FALSE, FALSE); gnt_box_set_toplevel(GNT_BOX(win), TRUE); gnt_box_set_pad(GNT_BOX(win), 0); tree = _list.tree = gnt_tree_new(); gnt_box_add_widget(GNT_BOX(win), tree); } static void show_window_list() { GntWidget *tree, *win; GList *iter; if (window_list) return; setup__list(); window_list = &_list; win = window_list->window; tree = window_list->tree; gnt_box_set_title(GNT_BOX(win), "Window List"); for (iter = focus_list; iter; iter = iter->next) { GntBox *box = GNT_BOX(iter->data); gnt_tree_add_row_last(GNT_TREE(tree), box, gnt_tree_create_row(GNT_TREE(tree), box->title), NULL); update_window_in_list(GNT_WIDGET(box)); } gnt_tree_set_selected(GNT_TREE(tree), ordered->data); g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(window_list_activate), NULL); gnt_tree_set_col_width(GNT_TREE(tree), 0, getmaxx(stdscr) / 3); gnt_widget_set_size(tree, 0, getmaxy(stdscr) / 2); gnt_widget_set_position(win, getmaxx(stdscr) / 3, getmaxy(stdscr) / 4); lock_focus_list = 1; gnt_widget_show(win); lock_focus_list = 0; } static void shift_window(GntWidget *widget, int dir) { GList *all = focus_list; GList *list = g_list_find(all, widget); int length, pos; if (!list) return; length = g_list_length(all); pos = g_list_position(all, list); pos += dir; if (dir > 0) pos++; if (pos < 0) pos = length; else if (pos > length) pos = 0; all = g_list_insert(all, widget, pos); all = g_list_delete_link(all, list); focus_list = all; draw_taskbar(FALSE); } static void dump_screen() { int x, y; chtype old = 0, now = 0; FILE *file = fopen("dump.html", "w"); fprintf(file, "<pre>"); for (y = 0; y < getmaxy(stdscr); y++) { for (x = 0; x < getmaxx(stdscr); x++) { char ch; now = mvwinch(curscr, y, x); ch = now & A_CHARTEXT; now ^= ch; #define CHECK(attr, start, end) \ do \ { \ if (now & attr) \ { \ if (!(old & attr)) \ fprintf(file, start); \ } \ else if (old & attr) \ { \ fprintf(file, end); \ } \ } while (0) CHECK(A_BOLD, "<b>", "</b>"); CHECK(A_UNDERLINE, "<u>", "</u>"); CHECK(A_BLINK, "<blink>", "</blink>"); if ((now & A_COLOR) != (old & A_COLOR) || (now & A_REVERSE) != (old & A_REVERSE)) { int ret; short fgp, bgp, r, g, b; struct { int r, g, b; } fg, bg; ret = pair_content(PAIR_NUMBER(now & A_COLOR), &fgp, &bgp); if (fgp == -1) fgp = COLOR_BLACK; if (bgp == -1) bgp = COLOR_WHITE; if (now & A_REVERSE) fgp ^= bgp ^= fgp ^= bgp; /* *wink* */ ret = color_content(fgp, &r, &g, &b); fg.r = r; fg.b = b; fg.g = g; ret = color_content(bgp, &r, &g, &b); bg.r = r; bg.b = b; bg.g = g; #define ADJUST(x) (x = x * 255 / 1000) ADJUST(fg.r); ADJUST(fg.g); ADJUST(fg.b); ADJUST(bg.r); ADJUST(bg.b); ADJUST(bg.g); if (x) fprintf(file, "</span>"); fprintf(file, "<span style=\"background:#%02x%02x%02x;color:#%02x%02x%02x\">", bg.r, bg.g, bg.b, fg.r, fg.g, fg.b); } if (now & A_ALTCHARSET) { switch (ch) { case 'q': ch = '-'; break; case 't': case 'u': case 'x': ch = '|'; break; case 'v': case 'w': case 'l': case 'm': case 'k': case 'j': case 'n': ch = '+'; break; case '-': ch = '^'; break; case '.': ch = 'v'; break; case 'a': ch = '#'; break; default: ch = ' '; break; } } if (ch == '&') fprintf(file, "&"); else if (ch == '<') fprintf(file, "<"); else if (ch == '>') fprintf(file, ">"); else fprintf(file, "%c", ch); old = now; } fprintf(file, "</span>\n"); old = 0; } fprintf(file, "</pre>"); fclose(file); } static void refresh_node(GntWidget *widget, GntNode *node, gpointer null) { int x, y, w, h; int nw, nh; gnt_widget_get_position(widget, &x, &y); gnt_widget_get_size(widget, &w, &h); if (x + w >= X_MAX) x = MAX(0, X_MAX - w); if (y + h >= Y_MAX) y = MAX(0, Y_MAX - h); gnt_screen_move_widget(widget, x, y); nw = MIN(w, X_MAX); nh = MIN(h, Y_MAX); if (nw != w || nh != h) gnt_screen_resize_widget(widget, nw, nh); } /** * Mouse support: * - bring a window on top if you click on its taskbar * - click on the top-bar of the active window and drag+drop to move a window * - click on a window to bring it to focus * - allow scrolling in tree/textview on wheel-scroll event * - click to activate button or select a row in tree * wishlist: * - have a little [X] on the windows, and clicking it will close that window. */ static gboolean detect_mouse_action(const char *buffer) { int x, y; static enum { MOUSE_NONE, MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE } button = MOUSE_NONE; static GntWidget *remember = NULL; static int offset = 0; GntMouseEvent event; GntWidget *widget = NULL; PANEL *p = NULL; if (!ordered || buffer[0] != 27) return FALSE; buffer++; if (strlen(buffer) < 5) return FALSE; x = buffer[3]; y = buffer[4]; if (x < 0) x += 256; if (y < 0) y += 256; x -= 33; y -= 33; while ((p = panel_below(p)) != NULL) { const GntNode *node = panel_userptr(p); GntWidget *wid; if (!node) continue; wid = node->me; if (x >= wid->priv.x && x < wid->priv.x + wid->priv.width) { if (y >= wid->priv.y && y < wid->priv.y + wid->priv.height) { widget = wid; break; } } } if (strncmp(buffer, "[M ", 3) == 0) { /* left button down */ /* Bring the window you clicked on to front */ /* If you click on the topbar, then you can drag to move the window */ event = GNT_LEFT_MOUSE_DOWN; } else if (strncmp(buffer, "[M\"", 3) == 0) { /* right button down */ event = GNT_RIGHT_MOUSE_DOWN; } else if (strncmp(buffer, "[M!", 3) == 0) { /* middle button down */ event = GNT_MIDDLE_MOUSE_DOWN; } else if (strncmp(buffer, "[M`", 3) == 0) { /* wheel up*/ event = GNT_MOUSE_SCROLL_UP; } else if (strncmp(buffer, "[Ma", 3) == 0) { /* wheel down */ event = GNT_MOUSE_SCROLL_DOWN; } else if (strncmp(buffer, "[M#", 3) == 0) { /* button up */ event = GNT_MOUSE_UP; } else return FALSE; if (wm.mouse_clicked && wm.mouse_clicked(event, x, y, widget)) return TRUE; if (event == GNT_LEFT_MOUSE_DOWN && widget && widget != _list.window && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) { if (widget != ordered->data) { GntWidget *w = ordered->data; ordered = g_list_bring_to_front(ordered, widget); wm.give_focus(ordered->data); gnt_widget_set_focus(w, FALSE); } if (y == widget->priv.y) { offset = x - widget->priv.x; remember = widget; button = MOUSE_LEFT; } } else if (event == GNT_MOUSE_UP) { if (button == MOUSE_NONE && y == getmaxy(stdscr) - 1) { /* Clicked on the taskbar */ int n = g_list_length(focus_list); if (n) { int width = getmaxx(stdscr) / n; switch_window_n(x / width); } } else if (button == MOUSE_LEFT && remember) { x -= offset; if (x < 0) x = 0; if (y < 0) y = 0; gnt_screen_move_widget(remember, x, y); refresh_node(remember, NULL, NULL); } button = MOUSE_NONE; remember = NULL; offset = 0; } gnt_widget_clicked(widget, event, x, y); return TRUE; /* XXX: this should be TRUE */ } #ifndef NO_WIDECHAR static int widestringwidth(wchar_t *wide) { int len, ret; char *string; len = wcstombs(NULL, wide, 0) + 1; string = g_new0(char, len); wcstombs(string, wide, len); ret = gnt_util_onscreen_width(string, NULL); g_free(string); return ret; } #endif /* Returns the onscreen width of the character at the position */ static int reverse_char(WINDOW *d, int y, int x, gboolean set) { #define DECIDE(ch) (set ? ((ch) | A_REVERSE) : ((ch) & ~A_REVERSE)) #ifdef NO_WIDECHAR chtype ch; ch = mvwinch(d, y, x); mvwaddch(d, y, x, DECIDE(ch)); return 1; #else cchar_t ch; int wc = 1; if (mvwin_wch(d, y, x, &ch) == OK) { wc = widestringwidth(ch.chars); ch.attr = DECIDE(ch.attr); ch.attr &= WA_ATTRIBUTES; /* XXX: This is a workaround for a bug */ mvwadd_wch(d, y, x, &ch); } return wc; #endif } static void window_reverse(GntWidget *win, gboolean set) { int i; int w, h; WINDOW *d; if (GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_NO_BORDER)) return; d = win->window; gnt_widget_get_size(win, &w, &h); if (gnt_widget_has_shadow(win)) { --w; --h; } /* the top and bottom */ for (i = 0; i < w; i += reverse_char(d, 0, i, set)); for (i = 0; i < w; i += reverse_char(d, h-1, i, set)); /* the left and right */ for (i = 0; i < h; i += reverse_char(d, i, 0, set)); for (i = 0; i < h; i += reverse_char(d, i, w-1, set)); wrefresh(win->window); } static gboolean io_invoke(GIOChannel *source, GIOCondition cond, gpointer null) { char keys[256]; gboolean ret = FALSE; static GntKeyPressMode mode = GNT_KP_MODE_NORMAL; const char *buffer; int rd = read(STDIN_FILENO, keys, sizeof(keys) - 1); if (rd < 0) { endwin(); printf("ERROR: %s\n", strerror(errno)); printf("File descriptor is: %d\n\nGIOChannel is: %p", STDIN_FILENO, source); exit(1); } else if (rd == 0) { endwin(); printf("EOF\n"); exit(1); } event_stack = TRUE; keys[rd] = 0; if (keys[0] == 27 && keys[1] == 'd' && keys[2] == 0) { /* This dumps the screen contents in an html file */ dump_screen(); } gnt_keys_refine(keys); if (mouse_enabled && detect_mouse_action(keys)) return TRUE; if (wm.key_pressed) { buffer = wm.key_pressed(keys); if (buffer == NULL) { event_stack = FALSE; return TRUE; } } else buffer = keys; if (mode == GNT_KP_MODE_NORMAL) { if (menu) { ret = gnt_widget_key_pressed(GNT_WIDGET(menu), buffer); } else if (ordered) { ret = gnt_widget_key_pressed(ordered->data, buffer); } if (!ret) { if (buffer[0] == 27) { /* Some special key has been pressed */ if (strcmp(buffer+1, GNT_KEY_POPUP) == 0) {} else if (strcmp(buffer + 1, "c") == 0) { /* Alt + c was pressed. I am going to use it to close a window. */ if (ordered) { gnt_widget_destroy(ordered->data); } } else if (strcmp(buffer + 1, "q") == 0) { /* I am going to use Alt + q to quit. */ g_main_loop_quit(loop); } else if (strcmp(buffer + 1, "n") == 0) { /* Alt + n to go to the next window */ switch_window(1); } else if (strcmp(buffer + 1, "p") == 0) { /* Alt + p to go to the previous window */ switch_window(-1); } else if (strcmp(buffer + 1, "m") == 0 && focus_list) { /* Move a window */ mode = GNT_KP_MODE_MOVE; window_reverse(ordered->data, TRUE); } else if (strcmp(buffer + 1, "w") == 0 && focus_list) { /* Window list */ mode = GNT_KP_MODE_WINDOW_LIST; show_window_list(); } else if (strcmp(buffer + 1, "a") == 0) { mode = GNT_KP_MODE_WINDOW_LIST; show_actions_list(); } else if (strcmp(buffer + 1, "r") == 0 && focus_list) { /* Resize window */ mode = GNT_KP_MODE_RESIZE; window_reverse(ordered->data, TRUE); } else if (strcmp(buffer + 1, ",") == 0 && focus_list) { /* Re-order the list of windows */ shift_window(ordered->data, -1); } else if (strcmp(buffer + 1, ".") == 0 && focus_list) { shift_window(ordered->data, 1); } else if (strcmp(buffer + 1, "l") == 0) { refresh_screen(); } else if (strlen(buffer) == 2 && isdigit(*(buffer + 1))) { int n = *(buffer + 1) - '0'; if (n == 0) n = 10; switch_window_n(n - 1); } } } } else if (mode == GNT_KP_MODE_MOVE && focus_list) { if (buffer[0] == 27) { gboolean changed = FALSE; int x, y, w, h; GntWidget *widget = GNT_WIDGET(ordered->data); gnt_widget_get_position(widget, &x, &y); gnt_widget_get_size(widget, &w, &h); if (strcmp(buffer + 1, GNT_KEY_LEFT) == 0) { if (x > X_MIN) { x--; changed = TRUE; } } else if (strcmp(buffer + 1, GNT_KEY_RIGHT) == 0) { if (x + w < X_MAX) { x++; changed = TRUE; } } else if (strcmp(buffer + 1, GNT_KEY_UP) == 0) { if (y > Y_MIN) { y--; changed = TRUE; } } else if (strcmp(buffer + 1, GNT_KEY_DOWN) == 0) { if (y + h < Y_MAX) { y++; changed = TRUE; } } else if (buffer[1] == 0) { mode = GNT_KP_MODE_NORMAL; window_reverse(widget, FALSE); } if (changed) { gnt_screen_move_widget(widget, x, y); } } else if (*buffer == '\r') { mode = GNT_KP_MODE_NORMAL; window_reverse(ordered->data, FALSE); } } else if (mode == GNT_KP_MODE_WINDOW_LIST && _list.window) { gnt_widget_key_pressed(_list.window, buffer); if (buffer[0] == '\r' || (buffer[0] == 27 && buffer[1] == 0)) { mode = GNT_KP_MODE_NORMAL; lock_focus_list = 1; gnt_widget_destroy(_list.window); _list.window = NULL; _list.tree = NULL; lock_focus_list = 0; window_list = NULL; action_list = NULL; } } else if (mode == GNT_KP_MODE_RESIZE) { if (buffer[0] == '\r' || (buffer[0] == 27 && buffer[1] == 0)) { mode = GNT_KP_MODE_NORMAL; window_reverse(ordered->data, FALSE); } else if (buffer[0] == 27) { GntWidget *widget = ordered->data; gboolean changed = FALSE; int width, height; gnt_widget_get_size(widget, &width, &height); if (strcmp(buffer + 1, GNT_KEY_DOWN) == 0) { if (widget->priv.y + height < Y_MAX) { height++; changed = TRUE; } } else if (strcmp(buffer + 1, GNT_KEY_UP) == 0) { height--; changed = TRUE; } else if (strcmp(buffer + 1, GNT_KEY_LEFT) == 0) { width--; changed = TRUE; } else if (strcmp(buffer + 1, GNT_KEY_RIGHT) == 0) { if (widget->priv.x + width < X_MAX) { width++; changed = TRUE; } } if (changed) { gnt_screen_resize_widget(widget, width, height); window_reverse(widget, TRUE); } } } event_stack = FALSE; return TRUE; } static gboolean refresh_screen() { endwin(); refresh(); X_MAX = getmaxx(stdscr); Y_MAX = getmaxy(stdscr) - 1; g_hash_table_foreach(nodes, (GHFunc)refresh_node, NULL); update_screen(NULL); draw_taskbar(TRUE); return FALSE; } /* Xerox */ static void clean_pid(void) { int status; pid_t pid; do { pid = waitpid(-1, &status, WNOHANG); } while (pid != 0 && pid != (pid_t)-1); if ((pid == (pid_t) - 1) && (errno != ECHILD)) { char errmsg[BUFSIZ]; g_snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid); perror(errmsg); } } static void sighandler(int sig) { switch (sig) { #ifdef SIGWINCH case SIGWINCH: werase(stdscr); wrefresh(stdscr); g_idle_add(refresh_screen, NULL); signal(SIGWINCH, sighandler); break; #endif case SIGCHLD: clean_pid(); signal(SIGCHLD, sighandler); break; } } static void init_wm() { const char *name = gnt_style_get(GNT_STYLE_WM); gpointer handle; if (!name || !*name) return; handle = g_module_open(name, G_MODULE_BIND_LAZY); if (handle) { gboolean (*init)(GntWM *); if (g_module_symbol(handle, "gntwm_init", (gpointer)&init)) { init(&wm); } } } void gnt_init() { static GIOChannel *channel = NULL; char *filename; int result; const char *locale; if (channel) return; channel = g_io_channel_unix_new(STDIN_FILENO); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); #if 0 g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL ); #endif result = g_io_add_watch_full(channel, G_PRIORITY_HIGH, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI | G_IO_NVAL), io_invoke, NULL, NULL); locale = setlocale(LC_ALL, ""); #if 0 g_io_channel_unref(channel); /* Apparently this causes crash for some people */ #endif if (locale && (strstr(locale, "UTF") || strstr(locale, "utf"))) ascii_only = FALSE; else ascii_only = TRUE; initscr(); typeahead(-1); noecho(); curs_set(0); gnt_init_styles(); filename = g_build_filename(g_get_home_dir(), ".gntrc", NULL); gnt_style_read_configure_file(filename); g_free(filename); gnt_init_colors(); X_MIN = 0; Y_MIN = 0; X_MAX = getmaxx(stdscr); Y_MAX = getmaxy(stdscr) - 1; nodes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_node); wbkgdset(stdscr, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); refresh(); #ifdef ALL_MOUSE_EVENTS if ((mouse_enabled = gnt_style_get_bool(GNT_STYLE_MOUSE, FALSE))) mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL); #endif wbkgdset(stdscr, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); werase(stdscr); wrefresh(stdscr); #ifdef SIGWINCH signal(SIGWINCH, sighandler); #endif signal(SIGCHLD, sighandler); signal(SIGPIPE, SIG_IGN); g_type_init(); init_wm(); } void gnt_main() { loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(loop); } /********************************* * Stuff for 'window management' * *********************************/ static void free_node(gpointer data) { GntNode *node = data; hide_panel(node->panel); del_panel(node->panel); g_free(node); } void gnt_screen_occupy(GntWidget *widget) { GntNode *node; while (widget->parent) widget = widget->parent; if (g_hash_table_lookup(nodes, widget)) return; /* XXX: perhaps _update instead? */ node = g_new0(GntNode, 1); node->me = widget; g_hash_table_replace(nodes, widget, node); refresh_node(widget, node, NULL); if (window_list) { if ((GNT_IS_BOX(widget) && GNT_BOX(widget)->title) && window_list->window != widget && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS)) { gnt_tree_add_row_last(GNT_TREE(window_list->tree), widget, gnt_tree_create_row(GNT_TREE(window_list->tree), GNT_BOX(widget)->title), NULL); update_window_in_list(widget); } } update_screen(NULL); } void gnt_screen_release(GntWidget *widget) { GntNode *node; gnt_screen_remove_widget(widget); node = g_hash_table_lookup(nodes, widget); if (node == NULL) /* Yay! Nothing to do. */ return; if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_DESTROYING) && wm.close_window) wm.close_window(widget); g_hash_table_remove(nodes, widget); if (window_list) { gnt_tree_remove(GNT_TREE(window_list->tree), widget); } update_screen(NULL); } void gnt_screen_update(GntWidget *widget) { GntNode *node; while (widget->parent) widget = widget->parent; if (!GNT_IS_MENU(widget)) gnt_box_sync_children(GNT_BOX(widget)); node = g_hash_table_lookup(nodes, widget); if (node && !node->panel) { if (wm.new_window && node->me != _list.window) node->panel = wm.new_window(node->me); else node->panel = new_panel(node->me->window); set_panel_userptr(node->panel, node); if (!GNT_WIDGET_IS_FLAG_SET(node->me, GNT_WIDGET_TRANSIENT)) { if (!g_object_get_data(G_OBJECT(node->me), "give_focus")) { bottom_panel(node->panel); /* New windows should not grab focus */ gnt_widget_set_urgent(node->me); } else { bring_on_top(node->me); } } } if (_list.window) { GntNode *nd = g_hash_table_lookup(nodes, _list.window); top_panel(nd->panel); } update_screen(NULL); } gboolean gnt_widget_has_focus(GntWidget *widget) { GntWidget *w; if (!widget) return FALSE; if (GNT_IS_MENU(widget)) return TRUE; w = widget; while (widget->parent) widget = widget->parent; if (widget == _list.window) return TRUE; if (ordered && ordered->data == widget) { if (GNT_IS_BOX(widget) && (GNT_BOX(widget)->active == w || widget == w)) return TRUE; } return FALSE; } void gnt_widget_set_urgent(GntWidget *widget) { while (widget->parent) widget = widget->parent; if (ordered && ordered->data == widget) return; GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_URGENT); if (wm.window_update) { GntNode *node = g_hash_table_lookup(nodes, widget); wm.window_update(node ? node->panel : NULL, widget); } draw_taskbar(FALSE); } void gnt_quit() { gnt_uninit_colors(); gnt_uninit_styles(); endwin(); } gboolean gnt_ascii_only() { return ascii_only; } void gnt_screen_resize_widget(GntWidget *widget, int width, int height) { if (widget->parent == NULL) { GntNode *node = g_hash_table_lookup(nodes, widget); if (!node) return; if (wm.window_resize_confirm && !wm.window_resize_confirm(widget, &width, &height)) return; hide_panel(node->panel); gnt_widget_set_size(widget, width, height); gnt_widget_draw(widget); if (wm.window_resized) node->panel = wm.window_resized(node->panel, widget); else replace_panel(node->panel, widget->window); show_panel(node->panel); update_screen(NULL); } } void gnt_screen_move_widget(GntWidget *widget, int x, int y) { GntNode *node = g_hash_table_lookup(nodes, widget); if (wm.window_move_confirm && !wm.window_move_confirm(widget, &x, &y)) return; gnt_widget_set_position(widget, x, y); move_panel(node->panel, y, x); if (wm.window_moved) wm.window_moved(node->panel, widget); update_screen(NULL); } void gnt_screen_rename_widget(GntWidget *widget, const char *text) { gnt_box_set_title(GNT_BOX(widget), text); gnt_widget_draw(widget); if (wm.window_update) { GntNode *node = g_hash_table_lookup(nodes, widget); wm.window_update(node ? node->panel : NULL, widget); } draw_taskbar(FALSE); } /** * An application can register actions which will show up in a 'start-menu' like popup */ typedef struct _GnAction { const char *label; void (*callback)(); } GntAction; static GList *actions; void gnt_register_action(const char *label, void (*callback)()) { GntAction *action = g_new0(GntAction, 1); action->label = g_strdup(label); action->callback = callback; actions = g_list_append(actions, action); } static void action_list_activate(GntTree *tree, gpointer null) { GntAction *action = gnt_tree_get_selection_data(tree); action->callback(); } static int compare_action(gconstpointer p1, gconstpointer p2) { const GntAction *a1 = p1; const GntAction *a2 = p2; return g_utf8_collate(a1->label, a2->label); } static void show_actions_list() { GntWidget *tree, *win; GList *iter; int h; if (action_list) return; setup__list(); action_list = &_list; win = action_list->window; tree = action_list->tree; gnt_box_set_title(GNT_BOX(win), "Actions"); GNT_WIDGET_SET_FLAGS(tree, GNT_WIDGET_NO_BORDER); /* XXX: Do we really want this? */ gnt_tree_set_compare_func(GNT_TREE(tree), compare_action); for (iter = actions; iter; iter = iter->next) { GntAction *action = iter->data; gnt_tree_add_row_last(GNT_TREE(tree), action, gnt_tree_create_row(GNT_TREE(tree), action->label), NULL); } g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(action_list_activate), NULL); gnt_widget_set_size(tree, 0, g_list_length(actions)); gnt_widget_get_size(win, NULL, &h); gnt_widget_set_position(win, 0, getmaxy(stdscr) - 1 - h); lock_focus_list = 1; gnt_widget_show(win); lock_focus_list = 0; } static void reset_menu(GntWidget *widget, gpointer null) { menu = NULL; } gboolean gnt_screen_menu_show(gpointer newmenu) { if (menu) { /* For now, if a menu is being displayed, then another menu * can NOT take over. */ return FALSE; } menu = newmenu; gnt_widget_draw(GNT_WIDGET(menu)); g_signal_connect(G_OBJECT(menu), "hide", G_CALLBACK(reset_menu), NULL); return TRUE; }