Add support for the zoomed state to managed windows.
[gwm.git] / actions.c
blobbc7155d3aaf82dd782ab0f71ab78bac3a3a74c75
1 /*
2 * actions.c
4 * Part of gwm, the Gratuitous Window Manager,
5 * by Gary Wong, <gtw@gnu.org>.
7 * Copyright (C) 2009 Gary Wong
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of version 3 of the GNU General Public License as
11 * published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * $Id$
24 #include <config.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #if HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #include <xcb/xcb.h>
34 #include "gwm.h"
36 #include "actions.h"
37 #include "frame.h"
38 #include "managed.h"
39 #include "menu.h"
40 #include "window-table.h"
42 static void external_command( char *name, ... ) {
44 if( !fork() ) {
45 va_list val;
46 int n;
47 char **args;
49 va_start( val, name );
50 for( n = 0; va_arg( val, char * ); n++ )
52 va_end( val );
54 args = alloca( ( n + 2 ) * sizeof *args );
56 args[ 0 ] = name;
58 va_start( val, name );
59 n = 1;
60 while( ( args[ n++ ] = va_arg( val, char * ) ) )
62 va_end( val );
64 setpgid( 0, 0 );
66 execvp( name, args );
68 _exit( 1 );
72 extern void action_raise_lowest( struct gwm_window *window,
73 xcb_generic_event_t *ev,
74 union callback_param cp ) {
76 xcb_circulate_window( c, XCB_CIRCULATE_RAISE_LOWEST,
77 screens[ window->screen ]->root );
80 extern void action_stack_opposite( struct gwm_window *window,
81 xcb_generic_event_t *ev,
82 union callback_param cp ) {
84 if( window->type == WINDOW_FRAME ) {
85 uint32_t n = XCB_STACK_MODE_OPPOSITE;
87 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_STACK_MODE,
88 &n );
92 extern void action_root_menu( struct gwm_window *window,
93 xcb_generic_event_t *ev,
94 union callback_param cp ) {
96 /* FIXME this should be configurable, of course */
97 const struct menuitem root_menu[] = {
98 { "Map all icons", action_map_all_icons },
99 { "Raise lowest window", action_raise_lowest },
100 { "xterm", action_start_xterm },
101 { NULL },
102 { "Exit", action_exit }
105 popup_menu( window, ev, sizeof root_menu / sizeof *root_menu, root_menu );
108 extern void action_iconify_window( struct gwm_window *window,
109 xcb_generic_event_t *ev,
110 union callback_param cp ) {
112 if( window->type == WINDOW_FRAME )
113 window = window->u.frame.child;
115 if( window->type == WINDOW_MANAGED &&
116 window->u.managed.state == STATE_NORMAL )
117 normal_to_iconic( window );
120 extern void action_deiconify_window( struct gwm_window *window,
121 xcb_generic_event_t *ev,
122 union callback_param cp ) {
124 if( window->type == WINDOW_FRAME )
125 window = window->u.frame.child;
127 if( window->type == WINDOW_MANAGED &&
128 window->u.managed.state == STATE_ICONIC )
129 iconic_to_normal( window );
132 extern void action_max_window( struct gwm_window *window,
133 xcb_generic_event_t *ev,
134 union callback_param cp ) {
136 if( window->type == WINDOW_FRAME )
137 window = window->u.frame.child;
139 if( window->type == WINDOW_MANAGED )
140 set_managed_net_state( window,
141 atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ],
142 NET_STATE_TOGGLE );
145 extern void action_map_raise( struct gwm_window *window,
146 xcb_generic_event_t *ev,
147 union callback_param cp ) {
149 if( window->type == WINDOW_FRAME )
150 window = window->u.frame.child;
152 if( window->type == WINDOW_MANAGED ) {
153 uint32_t n;
155 n = XCB_STACK_MODE_ABOVE;
156 xcb_configure_window( c, window->u.managed.frame->w,
157 XCB_CONFIG_WINDOW_STACK_MODE, &n );
159 if( window->u.managed.state == STATE_ICONIC )
160 iconic_to_normal( window );
164 extern void action_map_all_icons( struct gwm_window *window,
165 xcb_generic_event_t *ev,
166 union callback_param cp ) {
168 int i;
170 for( i = 0; i < windows.used; i++ )
171 if( windows.values[ i ]->type == WINDOW_MANAGED &&
172 windows.values[ i ]->u.managed.state == STATE_ICONIC )
173 iconic_to_normal( windows.values[ i ] );
176 extern void action_start_xterm( struct gwm_window *window,
177 xcb_generic_event_t *ev,
178 union callback_param cp ) {
180 external_command( "xterm", NULL );
183 extern void action_window_menu( struct gwm_window *window,
184 xcb_generic_event_t *ev,
185 union callback_param cp ) {
187 /* FIXME this should be configurable, of course */
188 const struct menuitem window_menu[] = {
189 { "Iconify", action_iconify_window },
190 { "Raise/lower", action_stack_opposite },
191 { "Zoom", action_max_window }
194 popup_menu( window, ev, sizeof window_menu / sizeof *window_menu,
195 window_menu );
198 static xcb_window_t place_holder;
199 static int window_was_iconic, window_was_dead, dead_x, dead_y;
201 static void window_list_activate( struct gwm_window *window,
202 xcb_generic_event_t *ev,
203 union callback_param cp ) {
205 if( place_holder ) {
206 xcb_destroy_window( c, place_holder );
207 place_holder = XCB_NONE;
211 static void window_list_enter( struct gwm_window *window,
212 xcb_generic_event_t *ev,
213 union callback_param cp ) {
215 struct gwm_window *client;
217 if( ( client = lookup_window( cp.l ) ) ) {
218 uint32_t values[ 4 ];
219 int live_x, live_y;
221 /* Introduce another (unmapped and otherwise unused) window into the
222 stack to mark the original position of the client in the stacking
223 order. This might seem ugly, but an alternative scheme to
224 allow us to track the stacking order synchronously ourselves
225 would require another round trip to the server, which would be
226 worse. */
227 place_holder = xcb_generate_id( c );
228 xcb_create_window( c, 0, place_holder, screens[ client->screen ]->root,
229 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
230 screens[ client->screen ]->root_visual, 0, NULL );
231 values[ 0 ] = client->u.managed.frame->w;
232 values[ 1 ] = XCB_STACK_MODE_BELOW;
233 xcb_configure_window( c, place_holder, XCB_CONFIG_WINDOW_SIBLING |
234 XCB_CONFIG_WINDOW_STACK_MODE, values );
236 if( ( window_was_iconic = client->u.managed.state == STATE_ICONIC ) )
237 iconic_to_normal( client );
239 live_x = dead_x = client->u.managed.frame->u.frame.x;
240 live_y = dead_y = client->u.managed.frame->u.frame.y;
242 make_area_live( client->screen, &live_x, &live_y,
243 client->u.managed.frame->u.frame.width,
244 client->u.managed.frame->u.frame.height, 8, 8 );
246 if( ( window_was_dead = live_x != dead_x || live_y != dead_y ) ) {
247 values[ 0 ] = live_x;
248 values[ 1 ] = live_y;
249 values[ 2 ] = pointer_demux; /* the menu */
250 values[ 3 ] = XCB_STACK_MODE_BELOW;
251 } else {
252 values[ 0 ] = pointer_demux; /* the menu */
253 values[ 1 ] = XCB_STACK_MODE_BELOW;
256 xcb_configure_window( c, client->u.managed.frame->w,
257 window_was_dead ? XCB_CONFIG_WINDOW_X |
258 XCB_CONFIG_WINDOW_Y |
259 XCB_CONFIG_WINDOW_SIBLING |
260 XCB_CONFIG_WINDOW_STACK_MODE :
261 XCB_CONFIG_WINDOW_SIBLING |
262 XCB_CONFIG_WINDOW_STACK_MODE, values );
264 if( focus_frame != client->u.managed.frame ) {
265 deactivate_focus_frame();
267 focus_frame = client->u.managed.frame;
269 activate_focus_frame( event_time( ev ) );
274 static void window_list_leave( struct gwm_window *window,
275 xcb_generic_event_t *ev,
276 union callback_param cp ) {
278 struct gwm_window *client;
280 if( ( client = lookup_window( cp.l ) ) ) {
281 uint32_t values[ 4 ];
283 if( window_was_dead ) {
284 values[ 0 ] = dead_x;
285 values[ 1 ] = dead_y;
286 values[ 2 ] = place_holder;
287 values[ 3 ] = XCB_STACK_MODE_ABOVE;
288 } else {
289 values[ 0 ] = place_holder;
290 values[ 1 ] = XCB_STACK_MODE_ABOVE;
292 xcb_configure_window( c, client->u.managed.frame->w,
293 window_was_dead ?
294 XCB_CONFIG_WINDOW_X |
295 XCB_CONFIG_WINDOW_Y |
296 XCB_CONFIG_WINDOW_SIBLING |
297 XCB_CONFIG_WINDOW_STACK_MODE :
298 XCB_CONFIG_WINDOW_SIBLING |
299 XCB_CONFIG_WINDOW_STACK_MODE, values );
301 if( window_was_iconic )
302 normal_to_iconic( client );
304 if( focus_frame == client->u.managed.frame ) {
305 deactivate_focus_frame();
307 xcb_set_input_focus( c, XCB_INPUT_FOCUS_NONE,
308 XCB_INPUT_FOCUS_POINTER_ROOT,
309 event_time( ev ) );
311 focus_frame = NULL;
315 if( place_holder ) {
316 xcb_destroy_window( c, place_holder );
317 place_holder = XCB_NONE;
321 extern void action_window_list_menu( struct gwm_window *window,
322 xcb_generic_event_t *ev,
323 union callback_param cp ) {
325 int i, num_items;
327 struct menuitem *items;
329 for( i = 0, num_items = 0; i < num_screens; i++ ) {
330 xcb_window_t w = screens[ i ]->root | STACK_END;
332 if( i )
333 num_items++;
335 do {
336 struct gwm_window *window = lookup_window( w );
337 xcb_window_t next =
338 stack_lookup( &window_stack, w )->lower_window;
340 if( window && window->type == WINDOW_FRAME )
341 num_items++;
343 w = next;
344 } while( w != ( screens[ i ]->root | STACK_END ) );
347 items = alloca( num_items * sizeof *items );
349 for( i = 0, num_items = 0; i < num_screens; i++ ) {
350 xcb_window_t w = screens[ i ]->root | STACK_END;
352 if( i ) {
353 items[ num_items ].label = NULL;
355 num_items++;
358 do {
359 struct gwm_window *window = lookup_window( w );
360 xcb_window_t next =
361 stack_lookup( &window_stack, w )->lower_window;
363 if( window && window->type == WINDOW_FRAME ) {
364 struct gwm_window *managed = window->u.frame.child;
365 char *name = managed->u.managed.name ?
366 managed->u.managed.name : "(Untitled)";
368 if( managed->u.managed.state == STATE_ICONIC ) {
369 int len = strlen( name );
370 char *new = alloca( len + 3 );
372 new[ 0 ] = '[';
373 strcpy( new + 1, name );
374 new[ len + 1 ] = ']';
375 new[ len + 2 ] = 0;
377 name = new;
380 items[ num_items ].label = name;
381 items[ num_items ].action = window_list_activate;
382 items[ num_items ].enter_action = window_list_enter;
383 items[ num_items ].leave_action = window_list_leave;
384 items[ num_items ].cp.l = managed->w;
385 items[ num_items ].icon = managed->w;
387 num_items++;
390 w = next;
391 } while( w != ( screens[ i ]->root | STACK_END ) );
394 popup_menu( window, ev, num_items, items );
397 extern void action_exit( struct gwm_window *window, xcb_generic_event_t *ev,
398 union callback_param cp ) {
400 /* FIXME prompt for confirmation of exit */
402 signal_caught = -1;
405 /* FIXME make the current frame bindings (move, resize, close?) actions too */