/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ /* * (C) 2006-2013 Intel Corp * * SPDX-License-Identifier: GPL-2.0-or-later * * Authors: Ross Burton * Jorn Baayen * * Licensed under the GPL v2 or greater. */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #define DEFAULT_HEIGHT 32 /* Default panel height */ #define PADDING 4 /* Applet padding */ static GList *open_modules = NULL; /* List of open modules */ enum { ATOM_WM_STRUT_PARTIAL, ATOM_MB_STATE, ATOM_STATE_DOCT_TITLEBAR, ATOM_DOCK_TITLEBAR_DESKTOP, /* Counter */ ATOM_LAST }; static Atom atoms[ATOM_LAST]; /* Load applet @name with ID @id */ static GtkWidget * load_applet (const char *name, const char *id, GtkOrientation orientation) { const char *applet_path; char *path; GModule *module; GtkWidget * (* create_func) (const char *id, GtkOrientation orientation); /* Get MATCHBOX_PANEL_APPLET_PATH */ applet_path = g_getenv ("MATCHBOX_PANEL_APPLET_PATH"); if (!applet_path) applet_path = DEFAULT_APPLET_PATH; /* Create the path to the applet */ path = g_module_build_path (applet_path, name); /* Try and open it */ module = g_module_open (path, G_MODULE_BIND_LOCAL | G_MODULE_BIND_LAZY); if (!module) { g_warning ("Failed to load applet \"%s\" (%s).", name, g_module_error ()); g_free (path); return NULL; } g_free (path); /* Dig up the mb_panel_applet_create symbol */ if (!g_module_symbol (module, "mb_panel_applet_create", (gpointer) &create_func)) { g_warning ("Applet \"%s\" does not export the " "\"mb_panel_applet_create\" symbol.", name); g_module_close (module); return NULL; } /* Keep track of open modules */ open_modules = g_list_prepend (open_modules, module); /* Run mb_panel_applet_create */ return create_func (id, orientation); } /* Load the applets from @applets_desc into @box, packing them using * @pack_func */ static void load_applets (const char *applets_desc, GtkBox *box, void ( *pack_func) (GtkBox *box, GtkWidget *widget, gboolean expand, gboolean fill, guint padding), GtkOrientation orientation) { char **applets; int i; /* Check for NULL description */ if (!applets_desc) return; /* Parse description */ applets = g_strsplit (applets_desc, ",", -1); for (i = 0; applets[i]; i++) { char *s; char **bits; GtkWidget *applet; s = applets[i]; if (s == NULL || s[0] == '\0') continue; bits = g_strsplit (s, ":", 2); applet = load_applet (bits[0], bits[1], orientation); if (applet) pack_func (box, applet, FALSE, FALSE, PADDING); g_strfreev (bits); } g_strfreev (applets); } static void set_struts (GtkWidget *window, GtkPositionType edge, int size) { Display *xdisplay; int x, y, w, h; struct { long left; long right; long top; long bottom; long left_start_y; long left_end_y; long right_start_y; long right_end_y; long top_start_x; long top_end_x; long bottom_start_x; long bottom_end_x; } struts = { 0, }; xdisplay = GDK_SCREEN_XDISPLAY (gtk_widget_get_screen (window)); g_assert (xdisplay); gtk_window_get_position (GTK_WINDOW (window), &x, &y); gtk_window_get_size (GTK_WINDOW (window), &w, &h); switch (edge) { case GTK_POS_LEFT: struts.left = size; struts.left_start_y = y; struts.left_end_y = y + h; break; case GTK_POS_RIGHT: struts.right = size; struts.right_start_y = y; struts.right_end_y = y + h; break; case GTK_POS_TOP: struts.top = size; struts.top_start_x = x; struts.top_end_x = x + w; break; case GTK_POS_BOTTOM: struts.bottom = size; struts.bottom_start_x = x; struts.bottom_end_x = x + w; break; } gdk_error_trap_push (); XChangeProperty (xdisplay, GDK_WINDOW_XID (gtk_widget_get_window (window)), atoms[ATOM_WM_STRUT_PARTIAL], XA_CARDINAL, 32, PropModeReplace, (guchar *) &struts, 12); gdk_error_trap_pop_ignored (); } #if 0 static void screen_size_changed_cb (GdkScreen *screen, GtkWidget *window) { gint x, y, w, h; gint screen_width, screen_height; gtk_window_get_position (GTK_WINDOW (window), &x, &y); gtk_window_get_size (GTK_WINDOW (window), &w, &h); screen_width = gdk_screen_get_width (screen); screen_height = gdk_screen_get_height (screen); if (snap_right) x = screen_width - w; if (snap_bottom) y = screen_height - h; if (center_horizontal) x = (screen_width - w) / 2; if (center_vertical) y = (screen_height - h)/ 2; if (fullscreen) { if (w > h) { w = screen_width; x = 0; } else { h = screen_height; y = 0; } gtk_window_move (GTK_WINDOW (window), x, y); gtk_widget_set_size_request (window, w, h); gtk_window_resize (GTK_WINDOW (window), w, h); } else if (snap_right || snap_bottom || center_horizontal || center_vertical) { gtk_window_move (GTK_WINDOW (window), x, y); } set_struts (window, x, y, w, h); } #endif static void get_atoms (Display *xdisplay) { static const char *names[] = { "_NET_WM_STRUT_PARTIAL", "_MB_WM_STATE", "_MB_WM_STATE_DOCK_TITLEBAR", "_MB_DOCK_TITLEBAR_SHOW_ON_DESKTOP" }; XInternAtoms (xdisplay, (char**)names, G_N_ELEMENTS (names), False, atoms); } int main (int argc, char **argv) { GOptionContext *option_context; GOptionGroup *option_group; GError *error; char *start_applets = NULL, *end_applets = NULL; char *edge_string = NULL, *mode_string = NULL; enum { MODE_DOCK, MODE_TITLEBAR, MODE_WINDOW } mode = MODE_DOCK; int size = DEFAULT_HEIGHT; int screen_num = -1; int monitor_num = -1; GtkPositionType edge = GTK_POS_TOP; GtkWidget *window, *box, *frame; GdkDisplay *display; GdkScreen *screen; GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL; GdkRectangle screen_geom; /* TODO: add these as groups (applets / position) */ GOptionEntry option_entries[] = { { "start-applets", 0, 0, G_OPTION_ARG_STRING, &start_applets, N_("Applets to pack at the start"), N_("APPLET[:APPLET_ID] ...") }, { "end-applets", 0, 0, G_OPTION_ARG_STRING, &end_applets, N_("Applets to pack at the end"), N_("APPLET[:APPLET_ID] ...") }, { "screen", 'n', 0, G_OPTION_ARG_INT, &screen_num, N_("Screen number"), N_("SCREEN") }, { "monitor", 'm', 0, G_OPTION_ARG_INT, &monitor_num, N_("Monitor number"), N_("MONITOR") }, { "edge", 'e', 0, G_OPTION_ARG_STRING, &edge_string, N_("Panel edge"), N_("TOP|BOTTON|LEFT|RIGHT") }, { "size", 's', 0, G_OPTION_ARG_INT, &size, N_("Panel size"), N_("PIXELS")}, { "mode", 'm', 0, G_OPTION_ARG_STRING, &mode_string, N_("Panel mode"), N_("DOCK|TITLEBAR|WINDOW") }, { NULL } }; /* Make sure that GModule is supported */ if (!g_module_supported ()) { g_warning (_("GModule support not found, this is required for matchbox-panel to work")); return -1; } /* Set up command line handling */ option_context = g_option_context_new (NULL); option_group = g_option_group_new ("matchbox-panel", N_("Matchbox Panel"), N_("Matchbox Panel options"), NULL, NULL); g_option_group_add_entries (option_group, option_entries); g_option_context_set_main_group (option_context, option_group); g_option_context_add_group (option_context, gtk_get_option_group (TRUE)); /* Parse command line */ error = NULL; if (!g_option_context_parse (option_context, &argc, &argv, &error)) { g_option_context_free (option_context); g_warning ("%s", error->message); g_error_free (error); return 1; } g_option_context_free (option_context); if (edge_string) { if (g_ascii_strcasecmp (edge_string, "top") == 0) { edge = GTK_POS_TOP; } else if (g_ascii_strcasecmp (edge_string, "bottom") == 0) { edge = GTK_POS_BOTTOM; } else if (g_ascii_strcasecmp (edge_string, "left") == 0) { edge = GTK_POS_LEFT; } else if (g_ascii_strcasecmp (edge_string, "right") == 0) { edge = GTK_POS_RIGHT; } else { g_printerr ("Unparsable edge '%s', expecting top/bottom/left/right\n", edge_string); return 1; } g_free (edge_string); } if (mode_string) { if (g_ascii_strcasecmp (mode_string, "dock") == 0) { mode = MODE_DOCK; } else if (g_ascii_strcasecmp (mode_string, "titlebar") == 0) { mode = MODE_TITLEBAR; } else if (g_ascii_strcasecmp (mode_string, "window") == 0) { mode = MODE_WINDOW; } else { g_printerr ("Unparsable mode '%s', expecting dock/titlebar/window\n", mode_string); return 1; } g_free (mode_string); } /* Set app name */ g_set_application_name (_("Matchbox Panel")); display = gdk_display_get_default (); get_atoms (GDK_DISPLAY_XDISPLAY (display)); if (screen_num != -1) { screen = gdk_display_get_screen (display, screen_num); } else { screen = gdk_display_get_default_screen (display); } if (monitor_num == -1) { monitor_num = gdk_screen_get_primary_monitor (screen); } /* Note that this is bare monitor geometry and not the work area, so panels will overlap. */ gdk_screen_get_monitor_geometry (screen, monitor_num, &screen_geom); /* Create window */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_set_name (window, "MatchboxPanel"); gtk_window_set_has_resize_grip (GTK_WINDOW (window), FALSE); if (mode != MODE_WINDOW) { gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DOCK); gtk_window_set_accept_focus (GTK_WINDOW (window), FALSE); } /* Set size */ switch (mode) { case MODE_DOCK: /* TODO: hook this up to GdkScreen:size-changed */ /* Orientation and size */ switch (edge) { case GTK_POS_TOP: case GTK_POS_BOTTOM: orientation = GTK_ORIENTATION_HORIZONTAL; gtk_widget_set_size_request (window, screen_geom.width, size); break; case GTK_POS_LEFT: case GTK_POS_RIGHT: orientation = GTK_ORIENTATION_VERTICAL; gtk_widget_set_size_request (window, size, screen_geom.height); break; } /* Position */ switch (edge) { case GTK_POS_TOP: case GTK_POS_LEFT: gtk_window_move (GTK_WINDOW (window), screen_geom.x, screen_geom.y); break; case GTK_POS_RIGHT: gtk_window_move (GTK_WINDOW (window), screen_geom.x + screen_geom.width - size, screen_geom.y); break; case GTK_POS_BOTTOM: gtk_window_move (GTK_WINDOW (window), screen_geom.x, screen_geom.y + screen_geom.height - size); } break; case MODE_TITLEBAR: /* TODO */ break; case MODE_WINDOW: /* Set the size based on half the screen dimensions, and requested size */ switch (edge) { case GTK_POS_TOP: case GTK_POS_BOTTOM: orientation = GTK_ORIENTATION_HORIZONTAL; gtk_widget_set_size_request (window, screen_geom.width / 2, size); break; case GTK_POS_LEFT: case GTK_POS_RIGHT: orientation = GTK_ORIENTATION_VERTICAL; gtk_widget_set_size_request (window, size, screen_geom.height / 2); break; } break; } gtk_widget_realize (window); if (mode == MODE_DOCK) set_struts (window, edge, size); /* Add frame */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); gtk_container_add (GTK_CONTAINER (window), frame); gtk_widget_show (frame); /* Is this a horizontal or a vertical layout? */ if (orientation == GTK_ORIENTATION_HORIZONTAL) { gtk_widget_set_name (frame, "MatchboxPanelFrameHorizontal"); } else { gtk_widget_set_name (frame, "MatchboxPanelFrameVertical"); } box = gtk_box_new (orientation, 0); gtk_container_add (GTK_CONTAINER (frame), box); gtk_widget_show (box); #if 0 /* Do we want to display the panel in the Matchbox titlebar? */ if (in_titlebar) { XChangeProperty (GDK_SCREEN_XDISPLAY (screen), GDK_WINDOW_XID (gtk_widget_get_window (window)), atoms[0], XA_ATOM, 32, PropModeReplace, (unsigned char *) &atoms[1], 2); } else { g_signal_connect (screen, "size-changed", G_CALLBACK (screen_size_changed_cb), window); } #endif /* Load applets */ load_applets (start_applets, GTK_BOX (box), gtk_box_pack_start, orientation); load_applets (end_applets, GTK_BOX (box), gtk_box_pack_end, orientation); /* And go! */ gtk_widget_show (window); gtk_main (); /* Cleanup */ gtk_widget_destroy (window); while (open_modules) { g_module_close (open_modules->data); open_modules = g_list_delete_link (open_modules, open_modules); } return 0; }