/* * Gtk2 Based matchbox panel manager. * * Copyright 2003 Matthew Allum * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include Display *dpy; GtkListStore *list_store; GtkWidget *list_view; Window win_panel; Atom atoms[4]; GtkTreeIter IterSelected; char *atom_names[] = { "WM_PROTOCOLS", "_NET_WM_PING", "WM_DELETE_WINDOW", "_NET_CLIENT_LIST", "_NET_ACTIVE_WINDOW" }; #define WM_PROTOCOLS 0 #define _NET_WM_PING 1 #define WM_DELETE_WINDOW 2 #define _NET_CLIENT_LIST 3 #define _NET_ACTIVE_WINDOW 4 #define MB_REQ_CLIENT_ORDER 0 void items_reorder (GtkWidget *w, GtkWidget *list_view); Window util_get_panel_win (Display *dpy, int panel_num) { char tray_atom_spec[128]; Atom atom_sys_tray; Window result; snprintf(tray_atom_spec, 128, "_NET_SYSTEM_TRAY_S%i", panel_num); atom_sys_tray = XInternAtom(dpy, tray_atom_spec, False); XGrabServer (dpy); result = XGetSelectionOwner(dpy, atom_sys_tray); XUngrabServer (dpy); return result; } gchar * util_get_window_name (Display *dpy, Window w) { Atom actual_type; Atom net_wm_name_atom = XInternAtom (dpy, "_NET_WM_NAME", False); Atom utf8_string_atom = XInternAtom (dpy, "UTF8_STRING", False); int actual_format; unsigned long nitems, bytes_after; unsigned char *prop = NULL; gchar *name = NULL; XTextProperty text_prop; int rc; gdk_error_trap_push (); rc = XGetWindowProperty (dpy, w, net_wm_name_atom, 0, G_MAXLONG, False, utf8_string_atom, &actual_type, &actual_format, &nitems, &bytes_after, &prop); gdk_error_trap_pop (); if (rc != Success) return FALSE; if (nitems) { name = g_strdup (prop); XFree (prop); } else { if (XGetWMName(dpy, w, &text_prop)) { name = g_strdup((char *) text_prop.value); XFree((char *) text_prop.value); } else { XFetchName(dpy, w, (char **)&name); } } return name; } gboolean util_get_client_window_list (Display *dpy, Window win_panel, Window **wins_result, guint *n_wins_result) { Atom net_client_list_atom = XInternAtom (dpy, "_NET_CLIENT_LIST", False); Atom actual_type; int actual_format; unsigned long nitems, bytes_after = 0; unsigned char *prop = NULL; if (XGetWindowProperty (dpy, win_panel, net_client_list_atom, 0, G_MAXLONG, False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytes_after, &prop) != Success) return FALSE; /* FIXME: more checks */ *wins_result = (Window *)prop; *n_wins_result = (guint)nitems; return TRUE; } GdkPixbuf * util_get_window_icon (Display *dpy, Window w) { Atom actual_type; Atom net_wm_icon_atom = XInternAtom (dpy, "_NET_WM_ICON", False); int actual_format; unsigned long nitems, bytes_after; gulong *prop = NULL; int rc; GdkPixbuf *pixbuf = NULL; gdk_error_trap_push (); rc = XGetWindowProperty (dpy, w, net_wm_icon_atom, 0, G_MAXLONG, False, XA_CARDINAL, &actual_type, &actual_format, &nitems, &bytes_after, (guchar **)&prop); if (gdk_error_trap_pop ()) return FALSE; if (rc != Success) return FALSE; if (nitems) { guint w = prop[0], h = prop[1]; guint i; guchar *pixels = g_malloc (w * h * 4); guchar *p = pixels; for (i = 0; i < w * h; i++) { gulong l = prop[2 + i]; *(p++) = (l & 0x00ff0000) >> 16; *(p++) = (l & 0x0000ff00) >> 8; *(p++) = (l & 0x000000ff); *(p++) = (l & 0xff000000) >> 24; } pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8, w, h, w * 4, (GdkPixbufDestroyNotify)g_free, NULL); } if (prop) XFree (prop); return pixbuf; } gboolean item_move_down (GtkWidget *w, GtkWidget *list_view) { GtkTreeSelection *sel; GtkTreeIter iter, *next_iter; GtkTreeModel *model; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view)); if (gtk_tree_selection_get_selected (sel, &model, &iter)) { next_iter = gtk_tree_iter_copy (&iter); if (gtk_tree_model_iter_next (model, next_iter)) { gtk_list_store_move_after (list_store, &iter, next_iter); gtk_tree_iter_free(next_iter); items_reorder (w, list_view); } } } gboolean item_move_up (GtkWidget *w, GtkWidget *list_view) { GtkTreeSelection *sel; GtkTreeIter iter, prev_iter; GtkTreeModel *model; GtkTreePath *path; gchar *str = NULL; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view)); if (gtk_tree_selection_get_selected (sel, &model, &iter)) { path = gtk_tree_model_get_path (model, &iter); gtk_tree_path_prev(path); gtk_tree_model_get_iter (model, &prev_iter, path); str = gtk_tree_model_get_string_from_iter (model, &prev_iter); if (str[0] == '0') gtk_list_store_move_after (list_store, &prev_iter, &iter); else gtk_list_store_move_before (list_store, &iter, &prev_iter); g_free(str); gtk_tree_path_free(path); items_reorder (w, list_view); } } void item_add_new (Display *dpy, Window w) { GtkTreeIter iter; gchar *name = util_get_window_name (dpy, w); GdkPixbuf *icon = util_get_window_icon (dpy, w); gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, 0, name, 1, w, -1); if (icon) { GdkPixbuf *icons = gdk_pixbuf_scale_simple (icon, 16, 16, GDK_INTERP_BILINEAR); gdk_pixbuf_unref (icon); gtk_list_store_set (list_store, &iter, 2, icons, -1); } /* XXX: free up name ? */ } void items_reorder (GtkWidget *w, GtkWidget *list_view) { GtkTreeIter iter; int rows = 0, i = 0; Window *wins = NULL; if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter)) { do { rows++; } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter)); wins = malloc(sizeof(Window)*rows); gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter); do { gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter, 1, &wins[i], -1); i++; } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter)); XChangeProperty(dpy, win_panel, atoms[MB_REQ_CLIENT_ORDER], XA_WINDOW, 32, PropModeReplace, (unsigned char *)wins, rows); free(wins); } } void gui_populate () { Window *list; guint nr, i; GtkTreeIter iter, iter_selected; GtkTreeSelection *sel; GtkTreeModel *model; GtkTreePath *path_selected; Bool have_selection = False; char *p; if (util_get_client_window_list (dpy, util_get_panel_win (dpy, 0), &list, &nr) == FALSE) return; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view)); if (gtk_tree_selection_get_selected (sel, &model, &iter_selected)) { have_selection = True; path_selected = gtk_tree_model_get_path(model, &iter_selected); } p = g_malloc0 (nr); gtk_list_store_clear (list_store); for (i = 0; i < nr; i++) { if (p[i] == 0 ) { item_add_new(dpy, list[i]); } } if (have_selection) { gtk_tree_selection_select_path (sel, path_selected); gtk_tree_path_free(path_selected); } g_free (p); } void gui_setup (void) { GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL); GtkWidget *scrolled = gtk_scrolled_window_new (NULL, NULL); GtkWidget *vbox, *vbox_buttons, *hbox1, *hbox2; GtkWidget *apply_button, *up_button, *down_button; GtkWidget *img_up, *img_down; GtkWidget *notebook, *notebook_label; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkWidget *combo, *enable_button; GList *display_app_list = NULL; notebook = gtk_notebook_new (); gtk_notebook_set_tab_pos( GTK_NOTEBOOK (notebook), GTK_POS_TOP); gtk_container_set_border_width (GTK_CONTAINER (notebook), 6); gtk_container_add (GTK_CONTAINER (window), notebook ); /* reorder tab */ /* list */ list_store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT,G_TYPE_OBJECT); list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store)); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list_view), FALSE); renderer = gtk_cell_renderer_pixbuf_new (); column = gtk_tree_view_column_new_with_attributes ("Icon", renderer, "pixbuf", 2, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Name", renderer, "text", 0, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (scrolled), list_view); gtk_container_set_border_width (GTK_CONTAINER (scrolled), 6); vbox = gtk_vbox_new (FALSE, 2); /* main vbox */ notebook_label = gtk_label_new ("Order"); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, notebook_label); gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); hbox1 = gtk_hbox_new (FALSE, 2); /* list + up/down buttons container */ gtk_box_pack_start (GTK_BOX (vbox), hbox1, TRUE, TRUE, 0); /* for apply button */ gtk_box_pack_start (GTK_BOX (hbox1), scrolled, TRUE, TRUE, 0); /* Up / Down buttons */ vbox_buttons = gtk_vbox_new (FALSE, 2); /* Up/Down button box */ gtk_container_set_border_width (GTK_CONTAINER (vbox_buttons), 6); up_button = gtk_button_new (); down_button = gtk_button_new (); img_up = gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (up_button), img_up); img_down = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (down_button), img_down); gtk_box_pack_start (GTK_BOX (vbox_buttons), up_button, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox_buttons), down_button, FALSE, TRUE, 0); gtk_box_pack_end (GTK_BOX (hbox1), vbox_buttons, FALSE, TRUE, 0); /* hbox2 = gtk_hbox_new (FALSE, 1); gtk_box_pack_end (GTK_BOX (vbox), hbox2, FALSE, TRUE, 0); apply_button = gtk_button_new_from_stock (GTK_STOCK_APPLY); gtk_box_pack_end (GTK_BOX (hbox2), apply_button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (apply_button), "clicked", G_CALLBACK (items_reorder), list_view); */ g_signal_connect (G_OBJECT (up_button), "clicked", G_CALLBACK (item_move_up), list_view); g_signal_connect (G_OBJECT (down_button), "clicked", G_CALLBACK (item_move_down), list_view); g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL); #if 0 /* enable tab */ vbox = gtk_vbox_new (FALSE, 2); gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); /* FIXME: should be a menu not combo? */ combo = gtk_combo_new(); display_app_list = g_list_append (display_app_list, "String 2"); display_app_list = g_list_append (display_app_list, "String 2"); display_app_list = g_list_append (display_app_list, "String 2"); gtk_combo_set_popdown_strings (GTK_COMBO (combo), display_app_list); gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, TRUE, 0); enable_button = gtk_check_button_new_with_label ( "Enable" ); gtk_box_pack_start (GTK_BOX (vbox), enable_button, FALSE, TRUE, 0); notebook_label = gtk_label_new ("Display"); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, notebook_label); /* Position tab */ vbox = gtk_vbox_new (FALSE, 2); notebook_label = gtk_label_new ("Position"); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, notebook_label); #endif gtk_window_set_title (GTK_WINDOW (window), "Panel Manager"); gtk_widget_show_all (window); } GdkFilterReturn window_filter (GdkXEvent *xev, GdkEvent *gev, gpointer d) { XEvent *ev = (XEvent *)xev; Display *dpy = ev->xany.display; if (ev->xany.type == PropertyNotify && ev->xproperty.window == win_panel) { if (ev->xproperty.atom == atoms[_NET_CLIENT_LIST]) { gui_populate (dpy); } } return GDK_FILTER_CONTINUE; } int main (int argc, char **argv) { gtk_init (&argc, &argv); dpy = GDK_DISPLAY (); atoms[_NET_CLIENT_LIST] = XInternAtom (dpy, "_NET_CLIENT_LIST", False); atoms[MB_REQ_CLIENT_ORDER] = XInternAtom (dpy, "_MB_REQ_CLIENT_ORDER", False); gui_setup (); if ((win_panel = util_get_panel_win (dpy, 0)) == None) { fprintf(stderr, "mbpanelmgr: unable to find panel to manage!\n"); exit(1); } gui_populate (); gdk_window_add_filter (NULL, window_filter, NULL); XSelectInput (dpy, win_panel, PropertyChangeMask); gtk_main (); exit (0); }