Commit d0f6283c authored by rhp's avatar rhp
Browse files

workspace menu

parent 6631763c
......@@ -84,7 +84,9 @@ meta_display_open (const char *name)
"MOTIF_WM_HINTS",
"_NET_WM_STATE_SHADED",
"_NET_WM_STATE_MAXIMIZED_HORZ",
"_NET_WM_STATE_MAXIMIZED_VERT"
"_NET_WM_STATE_MAXIMIZED_VERT",
"_NET_WM_DESKTOP",
"_NET_NUMBER_OF_DESKTOPS"
};
Atom atoms[G_N_ELEMENTS(atom_names)];
......@@ -111,13 +113,29 @@ meta_display_open (const char *name)
display->name = g_strdup (XDisplayName (name));
display->xdisplay = xdisplay;
display->error_traps = NULL;
display->server_grab_count = 0;
display->workspaces = NULL;
/* we have to go ahead and do this so error handlers work */
all_displays = g_slist_prepend (all_displays, display);
meta_display_init_keys (display);
XInternAtoms (display->xdisplay, atom_names, G_N_ELEMENTS (atom_names),
False, atoms);
display->atom_net_wm_name = atoms[0];
display->atom_wm_protocols = atoms[1];
display->atom_wm_take_focus = atoms[2];
display->atom_wm_delete_window = atoms[3];
display->atom_wm_state = atoms[4];
display->atom_net_close_window = atoms[5];
display->atom_net_wm_state = atoms[6];
display->atom_motif_wm_hints = atoms[7];
display->atom_net_wm_state_shaded = atoms[8];
display->atom_net_wm_state_maximized_horz = atoms[9];
display->atom_net_wm_state_maximized_vert = atoms[10];
display->atom_net_wm_desktop = atoms[11];
display->atom_net_number_of_desktops = atoms[12];
screens = NULL;
i = 0;
......@@ -151,22 +169,6 @@ meta_display_open (const char *name)
display);
display->window_ids = g_hash_table_new (unsigned_long_hash, unsigned_long_equal);
display->server_grab_count = 0;
XInternAtoms (display->xdisplay, atom_names, G_N_ELEMENTS (atom_names),
False, atoms);
display->atom_net_wm_name = atoms[0];
display->atom_wm_protocols = atoms[1];
display->atom_wm_take_focus = atoms[2];
display->atom_wm_delete_window = atoms[3];
display->atom_wm_state = atoms[4];
display->atom_net_close_window = atoms[5];
display->atom_net_wm_state = atoms[6];
display->atom_motif_wm_hints = atoms[7];
display->atom_net_wm_state_shaded = atoms[8];
display->atom_net_wm_state_maximized_horz = atoms[9];
display->atom_net_wm_state_maximized_vert = atoms[10];
display->double_click_time = 250;
display->last_button_time = 0;
......@@ -551,6 +553,27 @@ event_queue_callback (MetaEventQueue *queue,
*/
meta_window_delete (window, CurrentTime);
}
else if (event->xclient.message_type ==
display->atom_net_wm_desktop)
{
int space;
MetaWorkspace *workspace;
space = event->xclient.data.l[0];
meta_verbose ("Request to move %s to screen workspace %d\n",
window->desc, space);
workspace =
meta_display_get_workspace_by_screen_index (display,
window->screen,
space);
if (workspace)
meta_window_change_workspace (window, workspace);
else
meta_verbose ("No such workspace %d for screen\n", space);
}
}
break;
case MappingNotify:
......
......@@ -50,6 +50,8 @@ struct _MetaDisplay
Atom atom_net_wm_state_shaded;
Atom atom_net_wm_state_maximized_horz;
Atom atom_net_wm_state_maximized_vert;
Atom atom_net_wm_desktop;
Atom atom_net_number_of_desktops;
/* This is the actual window from focus events,
* not the one we last set
......@@ -106,5 +108,4 @@ MetaWorkspace* meta_display_get_workspace_by_screen_index (MetaDisplay *displa
MetaScreen *screen,
int index);
#endif
......@@ -22,6 +22,7 @@
#include "menu.h"
#include "main.h"
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
typedef struct _MenuItem MenuItem;
typedef struct _MenuData MenuData;
......@@ -44,9 +45,10 @@ static void activate_cb (GtkWidget *menuitem, gpointer data);
static GtkWidget *menu = NULL;
static MenuItem menuitems[] = {
{ META_MESSAGE_MENU_DELETE, GTK_STOCK_CLOSE, N_("Close") },
{ META_MESSAGE_MENU_MINIMIZE, NULL, N_("Minimize") },
{ META_MESSAGE_MENU_MAXIMIZE, NULL, N_("Maximize") }
{ META_MESSAGE_MENU_DELETE, GTK_STOCK_CLOSE, N_("_Close") },
{ META_MESSAGE_MENU_MINIMIZE, NULL, N_("_Minimize") },
{ META_MESSAGE_MENU_MAXIMIZE, NULL, N_("Ma_ximize") },
{ META_MESSAGE_MENU_SHADE, NULL, N_("_Shade") }
};
static void
......@@ -71,6 +73,67 @@ popup_position_func (GtkMenu *menu,
*y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
}
static gint
get_num_desktops (void)
{
Atom type;
gint format;
gulong nitems;
gulong bytes_after;
gulong *num;
int result;
XGetWindowProperty (gdk_display, gdk_root_window,
gdk_atom_intern ("_NET_NUMBER_OF_DESKTOPS", FALSE),
0, G_MAXLONG,
False, XA_CARDINAL, &type, &format, &nitems,
&bytes_after, (guchar **)&num);
if (type != XA_CARDINAL)
return 0;
result = *num;
XFree (num);
return result;
}
static gulong
get_current_desktop (GdkWindow *window)
{
Atom type;
gint format;
gulong nitems;
gulong bytes_after;
gulong *num;
gulong result;
int err;
gdk_error_trap_push ();
type = None;
XGetWindowProperty (gdk_display, GDK_WINDOW_XID (window),
gdk_atom_intern ("_NET_WM_DESKTOP", FALSE),
0, G_MAXLONG,
False, XA_CARDINAL, &type, &format, &nitems,
&bytes_after, (guchar **)&num);
err = gdk_error_trap_pop ();
if (err != Success)
meta_ui_warning ("Error %d getting _NET_WM_DESKTOP\n", err);
if (type != XA_CARDINAL)
{
meta_ui_warning ("_NET_WM_DESKTOP has wrong type %s\n", gdk_atom_name (type));
return -1;
}
result = *num;
XFree (num);
return result;
}
void
meta_window_menu_show (gulong xwindow,
int root_x, int root_y,
......@@ -82,6 +145,8 @@ meta_window_menu_show (gulong xwindow,
int i;
GdkWindow *window;
GdkPoint *pt;
int n_workspaces;
int current_workspace;
if (menu)
gtk_widget_destroy (menu);
......@@ -110,7 +175,7 @@ meta_window_menu_show (gulong xwindow,
{
GtkWidget *image;
mi = gtk_image_menu_item_new_with_label (menuitems[i].label);
mi = gtk_image_menu_item_new_with_mnemonic (menuitems[i].label);
image = gtk_image_new_from_stock (menuitems[i].stock_id,
GTK_ICON_SIZE_MENU);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi),
......@@ -119,7 +184,7 @@ meta_window_menu_show (gulong xwindow,
}
else
{
mi = gtk_menu_item_new_with_label (menuitems[i].label);
mi = gtk_menu_item_new_with_mnemonic (menuitems[i].label);
}
if (insensitive & menuitems[i].op)
......@@ -142,6 +207,60 @@ meta_window_menu_show (gulong xwindow,
}
++i;
}
if (ops & META_MESSAGE_MENU_WORKSPACES)
{
n_workspaces = get_num_desktops ();
current_workspace = get_current_desktop (window);
meta_ui_warning ("Creating %d workspace menu current %d\n",
n_workspaces, current_workspace);
if (n_workspaces > 0 && current_workspace >= 0)
{
i = 0;
while (i < n_workspaces)
{
char *label;
GtkWidget *mi;
MenuData *md;
label = g_strdup_printf (_("Move to workspace _%d\n"),
i + 1);
mi = gtk_menu_item_new_with_mnemonic (label);
g_free (label);
if (current_workspace == i ||
insensitive & META_MESSAGE_MENU_WORKSPACES)
gtk_widget_set_sensitive (mi, FALSE);
md = g_new (MenuData, 1);
md->window = window;
md->op = META_MESSAGE_MENU_WORKSPACES;
g_object_set_data (G_OBJECT (mi),
"workspace",
GINT_TO_POINTER (i));
gtk_signal_connect (GTK_OBJECT (mi),
"activate",
GTK_SIGNAL_FUNC (activate_cb),
md);
gtk_menu_shell_append (GTK_MENU_SHELL (menu),
mi);
gtk_widget_show (mi);
++i;
}
}
}
else
meta_ui_warning ("not creating workspace menu\n");
gtk_signal_connect (GTK_OBJECT (menu),
"destroy",
......@@ -196,6 +315,58 @@ close_window (GdkWindow *window)
gdk_error_trap_pop ();
}
static void
wmspec_change_state (gboolean add,
GdkWindow *window,
GdkAtom state1,
GdkAtom state2)
{
XEvent xev;
Atom op;
if (add)
op = gdk_atom_intern ("_NET_WM_STATE_ADD", FALSE);
else
op = gdk_atom_intern ("_NET_WM_STATE_REMOVE", FALSE);
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = gdk_display;
xev.xclient.window = GDK_WINDOW_XID (window);
xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE);
xev.xclient.format = 32;
xev.xclient.data.l[0] = op;
xev.xclient.data.l[1] = state1;
xev.xclient.data.l[2] = state2;
XSendEvent (gdk_display, gdk_root_window, False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
static void
wmspec_change_desktop (GdkWindow *window,
gint desktop)
{
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = gdk_display;
xev.xclient.window = GDK_WINDOW_XID (window);
xev.xclient.message_type = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE);
xev.xclient.format = 32;
xev.xclient.data.l[0] = desktop;
xev.xclient.data.l[1] = 0;
xev.xclient.data.l[2] = 0;
XSendEvent (gdk_display, gdk_root_window, False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
static void
activate_cb (GtkWidget *menuitem, gpointer data)
{
......@@ -219,6 +390,23 @@ activate_cb (GtkWidget *menuitem, gpointer data)
gdk_error_trap_pop ();
break;
case META_MESSAGE_MENU_SHADE:
wmspec_change_state (TRUE, md->window,
gdk_atom_intern ("_NET_WM_STATE_SHADED", FALSE),
0);
break;
case META_MESSAGE_MENU_WORKSPACES:
{
int workspace;
workspace = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menuitem),
"workspace"));
wmspec_change_desktop (md->window, workspace);
}
break;
default:
meta_ui_warning (G_STRLOC": Unknown window op\n");
break;
......
......@@ -460,3 +460,25 @@ meta_screen_hide_tip (MetaScreen *screen)
screen->showing_tooltip = FALSE;
}
}
int
meta_screen_get_n_workspaces (MetaScreen *screen)
{
GList *tmp;
int i;
i = 0;
tmp = screen->display->workspaces;
while (tmp != NULL)
{
MetaWorkspace *w = tmp->data;
if (w->screen == screen)
++i;
tmp = tmp->next;
}
return i;
}
......@@ -77,6 +77,9 @@ void meta_screen_show_tip (MetaScreen *scree
const char *markup);
void meta_screen_hide_tip (MetaScreen *screen);
int meta_screen_get_n_workspaces (MetaScreen *screen);
#endif
......
......@@ -22,6 +22,7 @@
#include "menu.h"
#include "main.h"
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
typedef struct _MenuItem MenuItem;
typedef struct _MenuData MenuData;
......@@ -44,9 +45,10 @@ static void activate_cb (GtkWidget *menuitem, gpointer data);
static GtkWidget *menu = NULL;
static MenuItem menuitems[] = {
{ META_MESSAGE_MENU_DELETE, GTK_STOCK_CLOSE, N_("Close") },
{ META_MESSAGE_MENU_MINIMIZE, NULL, N_("Minimize") },
{ META_MESSAGE_MENU_MAXIMIZE, NULL, N_("Maximize") }
{ META_MESSAGE_MENU_DELETE, GTK_STOCK_CLOSE, N_("_Close") },
{ META_MESSAGE_MENU_MINIMIZE, NULL, N_("_Minimize") },
{ META_MESSAGE_MENU_MAXIMIZE, NULL, N_("Ma_ximize") },
{ META_MESSAGE_MENU_SHADE, NULL, N_("_Shade") }
};
static void
......@@ -71,6 +73,67 @@ popup_position_func (GtkMenu *menu,
*y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
}
static gint
get_num_desktops (void)
{
Atom type;
gint format;
gulong nitems;
gulong bytes_after;
gulong *num;
int result;
XGetWindowProperty (gdk_display, gdk_root_window,
gdk_atom_intern ("_NET_NUMBER_OF_DESKTOPS", FALSE),
0, G_MAXLONG,
False, XA_CARDINAL, &type, &format, &nitems,
&bytes_after, (guchar **)&num);
if (type != XA_CARDINAL)
return 0;
result = *num;
XFree (num);
return result;
}
static gulong
get_current_desktop (GdkWindow *window)
{
Atom type;
gint format;
gulong nitems;
gulong bytes_after;
gulong *num;
gulong result;
int err;
gdk_error_trap_push ();
type = None;
XGetWindowProperty (gdk_display, GDK_WINDOW_XID (window),
gdk_atom_intern ("_NET_WM_DESKTOP", FALSE),
0, G_MAXLONG,
False, XA_CARDINAL, &type, &format, &nitems,
&bytes_after, (guchar **)&num);
err = gdk_error_trap_pop ();
if (err != Success)
meta_ui_warning ("Error %d getting _NET_WM_DESKTOP\n", err);
if (type != XA_CARDINAL)
{
meta_ui_warning ("_NET_WM_DESKTOP has wrong type %s\n", gdk_atom_name (type));
return -1;
}
result = *num;
XFree (num);
return result;
}
void
meta_window_menu_show (gulong xwindow,
int root_x, int root_y,
......@@ -82,6 +145,8 @@ meta_window_menu_show (gulong xwindow,
int i;
GdkWindow *window;
GdkPoint *pt;
int n_workspaces;
int current_workspace;
if (menu)
gtk_widget_destroy (menu);
......@@ -110,7 +175,7 @@ meta_window_menu_show (gulong xwindow,
{
GtkWidget *image;
mi = gtk_image_menu_item_new_with_label (menuitems[i].label);
mi = gtk_image_menu_item_new_with_mnemonic (menuitems[i].label);
image = gtk_image_new_from_stock (menuitems[i].stock_id,
GTK_ICON_SIZE_MENU);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi),
......@@ -119,7 +184,7 @@ meta_window_menu_show (gulong xwindow,
}
else
{
mi = gtk_menu_item_new_with_label (menuitems[i].label);
mi = gtk_menu_item_new_with_mnemonic (menuitems[i].label);
}
if (insensitive & menuitems[i].op)
......@@ -142,6 +207,60 @@ meta_window_menu_show (gulong xwindow,
}
++i;
}
if (ops & META_MESSAGE_MENU_WORKSPACES)
{
n_workspaces = get_num_desktops ();
current_workspace = get_current_desktop (window);
meta_ui_warning ("Creating %d workspace menu current %d\n",
n_workspaces, current_workspace);
if (n_workspaces > 0 && current_workspace >= 0)
{
i = 0;
while (i < n_workspaces)
{
char *label;
GtkWidget *mi;
MenuData *md;
label = g_strdup_printf (_("Move to workspace _%d\n"),
i + 1);
mi = gtk_menu_item_new_with_mnemonic (label);
g_free (label);
if (current_workspace == i ||
insensitive & META_MESSAGE_MENU_WORKSPACES)
gtk_widget_set_sensitive (mi, FALSE);
md = g_new (MenuData, 1);
md->window = window;
md->op = META_MESSAGE_MENU_WORKSPACES;
g_object_set_data (G_OBJECT (mi),
"workspace",
GINT_TO_POINTER (i));
gtk_signal_connect (GTK_OBJECT (mi),
"activate",
GTK_SIGNAL_FUNC (activate_cb),
md);
gtk_menu_shell_append (GTK_MENU_SHELL (menu),
mi);
gtk_widget_show (mi);
++i;
}
}
}
else
meta_ui_warning ("not creating workspace menu\n");
gtk_signal_connect (GTK_OBJECT (menu),
"destroy",
......@@ -196,6 +315,58 @@ close_window (GdkWindow *window)
gdk_error_trap_pop ();
}
static void
wmspec_change_state (gboolean add,
GdkWindow *window,
GdkAtom state1,
GdkAtom state2)
{
XEvent xev;
Atom op;
if (add)
op = gdk_atom_intern ("_NET_WM_STATE_ADD", FALSE);
else
op = gdk_atom_intern ("_NET_WM_STATE_REMOVE", FALSE);
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = gdk_display;
xev.xclient.window = GDK_WINDOW_XID (window);
xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE);
xev.xclient.format = 32;
xev.xclient.data.l[0] = op;
xev.xclient.data.l[1] = state1;
xev.xclient.data.l[2] = state2;
XSendEvent (gdk_display, gdk_root_window, False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
static void
wmspec_change_desktop (GdkWindow *window,
gint desktop)
{
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = gdk_display;
xev.xclient.window = GDK_WINDOW_XID (window);
xev.xclient.message_type = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE);
xev.xclient.format = 32;
xev.xclient.data.l[0] = desktop;
xev.xclient.data.l[1] = 0;