comparison src/fullscreen.c @ 9:d907d608745f

Sync to GQview 1.5.9 release. ######## DO NOT BASE ENHANCEMENTS OR TRANSLATION UPDATES ON CODE IN THIS CVS! This CVS is never up to date with current development and is provided solely for reference purposes, please use the latest official release package when making any changes or translation updates. ########
author gqview
date Sat, 26 Feb 2005 00:13:35 +0000
parents
children 17acca639a86
comparison
equal deleted inserted replaced
8:e0d0593d519e 9:d907d608745f
1 /*
2 * GQview
3 * (C) 2004 John Ellis
4 *
5 * Author: John Ellis
6 *
7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
10 */
11
12
13 #include "gqview.h"
14 #include "fullscreen.h"
15
16 #include "image.h"
17 #include "ui_fileops.h"
18 #include "ui_menu.h"
19 #include "ui_misc.h"
20
21
22 enum {
23 FULLSCREEN_CURSOR_HIDDEN = 1 << 0,
24 FULLSCREEN_CURSOR_NORMAL = 1 << 1,
25 FULLSCREEN_CURSOR_BUSY = 1 << 2
26 };
27
28
29 /*
30 *----------------------------------------------------------------------------
31 * full screen functions
32 *----------------------------------------------------------------------------
33 */
34
35 static void clear_mouse_cursor(GtkWidget *widget, gint state)
36 {
37 if (!widget->window) return;
38
39 if (state & FULLSCREEN_CURSOR_BUSY)
40 {
41 GdkCursor *cursor;
42
43 cursor = gdk_cursor_new(GDK_WATCH);
44 gdk_window_set_cursor (widget->window, cursor);
45 gdk_cursor_unref(cursor);
46 }
47 else if (state & FULLSCREEN_CURSOR_NORMAL)
48 {
49 gdk_window_set_cursor (widget->window, NULL);
50 }
51 else
52 {
53 GdkCursor *cursor;
54 GdkPixmap *p;
55
56 p = gdk_bitmap_create_from_data(widget->window, "\0\0\0", 1, 1);
57
58 cursor = gdk_cursor_new_from_pixmap(p, p,
59 &widget->style->fg[GTK_STATE_ACTIVE],
60 &widget->style->bg[GTK_STATE_ACTIVE],
61 0, 0);
62
63 gdk_window_set_cursor (widget->window, cursor);
64
65 gdk_cursor_unref(cursor);
66 g_object_unref(p);
67 }
68 }
69
70 static gint fullscreen_hide_mouse_cb(gpointer data)
71 {
72 FullScreenData *fs = data;
73
74 if (fs->hide_mouse_id == -1) return FALSE;
75
76 fs->cursor_state &= ~FULLSCREEN_CURSOR_NORMAL;
77 if (!(fs->cursor_state & FULLSCREEN_CURSOR_BUSY)) clear_mouse_cursor(fs->window, fs->cursor_state);
78
79 fs->hide_mouse_id = -1;
80 return FALSE;
81 }
82
83 static void fullscreen_hide_mouse_disable(FullScreenData *fs)
84 {
85 if (fs->hide_mouse_id != -1)
86 {
87 g_source_remove(fs->hide_mouse_id);
88 fs->hide_mouse_id = -1;
89 }
90 }
91
92 static void fullscreen_hide_mouse_reset(FullScreenData *fs)
93 {
94 fullscreen_hide_mouse_disable(fs);
95 fs->hide_mouse_id = g_timeout_add(FULL_SCREEN_HIDE_MOUSE_DELAY, fullscreen_hide_mouse_cb, fs);
96 }
97
98 static gint fullscreen_mouse_moved(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
99 {
100 FullScreenData *fs = data;
101
102 if (!(fs->cursor_state & FULLSCREEN_CURSOR_NORMAL))
103 {
104 fs->cursor_state |= FULLSCREEN_CURSOR_NORMAL;
105 if (!(fs->cursor_state & FULLSCREEN_CURSOR_BUSY)) clear_mouse_cursor(fs->window, fs->cursor_state);
106 }
107 fullscreen_hide_mouse_reset(fs);
108
109 return FALSE;
110 }
111
112 static void fullscreen_busy_mouse_disable(FullScreenData *fs)
113 {
114 if (fs->busy_mouse_id != -1)
115 {
116 g_source_remove(fs->busy_mouse_id);
117 fs->busy_mouse_id = -1;
118 }
119 }
120
121 static void fullscreen_mouse_set_busy(FullScreenData *fs, gint busy)
122 {
123 fullscreen_busy_mouse_disable(fs);
124
125 if ((fs->cursor_state & FULLSCREEN_CURSOR_BUSY) == (busy)) return;
126
127 if (busy)
128 {
129 fs->cursor_state |= FULLSCREEN_CURSOR_BUSY;
130 }
131 else
132 {
133 fs->cursor_state &= ~FULLSCREEN_CURSOR_BUSY;
134 }
135
136 clear_mouse_cursor(fs->window, fs->cursor_state);
137 }
138
139 static gboolean fullscreen_mouse_set_busy_cb(gpointer data)
140 {
141 FullScreenData *fs = data;
142
143 fs->busy_mouse_id = -1;
144 fullscreen_mouse_set_busy(fs, TRUE);
145 return FALSE;
146 }
147
148 static void fullscreen_mouse_set_busy_idle(FullScreenData *fs)
149 {
150 if (fs->busy_mouse_id == -1)
151 {
152 fs->busy_mouse_id = g_timeout_add(FULL_SCREEN_BUSY_MOUSE_DELAY,
153 fullscreen_mouse_set_busy_cb, fs);
154 }
155 }
156
157 static void fullscreen_image_update_cb(ImageWindow *imd, gpointer data)
158 {
159 FullScreenData *fs = data;
160
161 if (fs->imd->il &&
162 fs->imd->pixbuf != fs->imd->il->pixbuf)
163 {
164 fullscreen_mouse_set_busy_idle(fs);
165 }
166 }
167
168 static void fullscreen_image_complete_cb(ImageWindow *imd, gint preload, gpointer data)
169 {
170 FullScreenData *fs = data;
171
172 if (!preload) fullscreen_mouse_set_busy(fs, FALSE);
173 }
174
175 #define XSCREENSAVER_BINARY "xscreensaver-command"
176 #define XSCREENSAVER_COMMAND "xscreensaver-command -deactivate >&- 2>&- &"
177
178 static void fullscreen_saver_deactivate(void)
179 {
180 static gint checked = FALSE;
181 static gint found = FALSE;
182
183 if (!checked)
184 {
185 checked = TRUE;
186 found = file_in_path(XSCREENSAVER_BINARY);
187 }
188
189 if (found)
190 {
191 system (XSCREENSAVER_COMMAND);
192 }
193 }
194
195 static gboolean fullscreen_saver_block_cb(gpointer data)
196 {
197 if (fullscreen_disable_saver)
198 {
199 fullscreen_saver_deactivate();
200 }
201
202 return TRUE;
203 }
204
205 static gint fullscreen_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer data)
206 {
207 FullScreenData *fs = data;
208
209 fullscreen_stop(fs);
210 return TRUE;
211 }
212
213 FullScreenData *fullscreen_start(GtkWidget *window, ImageWindow *imd,
214 void (*stop_func)(FullScreenData *, gpointer), gpointer stop_data)
215 {
216 FullScreenData *fs;
217 GdkScreen *screen;
218 gint same;
219 gint x, y;
220 gint w, h;
221 GdkGeometry geometry;
222
223 if (!window || !imd) return NULL;
224
225 fs = g_new0(FullScreenData, 1);
226
227 fs->hide_mouse_id = -1;
228 fs->busy_mouse_id = -1;
229 fs->cursor_state = FULLSCREEN_CURSOR_HIDDEN;
230
231 fs->normal_window = window;
232 fs->normal_imd = imd;
233
234 fs->stop_func = stop_func;
235 fs->stop_data = stop_data;
236
237 if (debug) printf("full screen requests screen %d\n", fullscreen_screen);
238 fullscreen_prefs_get_geometry(fullscreen_screen, window, &x, &y, &w, &h,
239 &screen, &same);
240
241 fs->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
242 gtk_window_set_wmclass(GTK_WINDOW(fs->window), "fullscreen", "GQview");
243
244 /* this requests no decorations, if you still have them complain to the window manager author(s) */
245 gtk_window_set_decorated(GTK_WINDOW(fs->window), FALSE);
246
247 if (fullscreen_screen < 0)
248 {
249 /* If we want control of the window size and position this is not what we want.
250 * GQview needs control of which monitor(s) to use for full screen.
251 */
252 gtk_window_fullscreen(GTK_WINDOW(fs->window));
253 }
254 else if (fullscreen_above)
255 {
256 /* request to be above other windows */
257 gtk_window_set_keep_above(GTK_WINDOW(fs->window), TRUE);
258 }
259
260 gtk_window_set_resizable(GTK_WINDOW(fs->window), FALSE);
261
262 gtk_window_set_screen(GTK_WINDOW(fs->window), screen);
263 gtk_container_set_border_width(GTK_CONTAINER(fs->window), 0);
264 g_signal_connect(G_OBJECT(fs->window), "delete_event",
265 G_CALLBACK(fullscreen_delete_cb), fs);
266
267 gtk_window_set_title(GTK_WINDOW(fs->window), _("GQview full screen"));
268
269 geometry.min_width = w;
270 geometry.min_height = h;
271 geometry.max_width = w;
272 geometry.max_height = h;
273 geometry.base_width = w;
274 geometry.base_height = h;
275 geometry.win_gravity = GDK_GRAVITY_STATIC;
276 /* By setting USER_POS and USER_SIZE, most window managers will
277 * not request positioning of the full screen window (for example twm).
278 *
279 * In addition, setting gravity to STATIC will result in the
280 * decorations of twm to not effect the requested window position,
281 * the decorations will simply be off screen, except in multi monitor setups :-/
282 */
283 gtk_window_set_geometry_hints(GTK_WINDOW(fs->window), fs->window, &geometry,
284 GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_BASE_SIZE |
285 GDK_HINT_WIN_GRAVITY |
286 GDK_HINT_USER_POS | GDK_HINT_USER_SIZE);
287
288 gtk_window_set_default_size(GTK_WINDOW(fs->window), w, h);
289 gtk_window_move(GTK_WINDOW(fs->window), x, y);
290
291 fs->imd = image_new(FALSE);
292
293 gtk_container_add(GTK_CONTAINER(fs->window), fs->imd->widget);
294
295 /* set background to black */
296 if (BLACK_BACKGROUND)
297 {
298 image_background_set_black(fs->imd, TRUE);
299 }
300
301 image_set_delay_flip(fs->imd, fullscreen_clean_flip);
302 image_auto_refresh(fs->imd, fs->normal_imd->auto_refresh_interval);
303
304 if (fullscreen_clean_flip)
305 {
306 image_set_update_func(fs->imd, fullscreen_image_update_cb, fs);
307 image_set_complete_func(fs->imd, fullscreen_image_complete_cb, fs);
308 }
309
310 gtk_widget_show(fs->imd->widget);
311
312 image_change_from_image(fs->imd, fs->normal_imd);
313
314 gtk_widget_show(fs->window);
315
316 /* for hiding the mouse */
317 g_signal_connect(G_OBJECT(fs->imd->image), "motion_notify_event",
318 G_CALLBACK(fullscreen_mouse_moved), fs);
319 clear_mouse_cursor(fs->window, fs->cursor_state);
320
321 /* set timer to block screen saver */
322 fs->saver_block_id = g_timeout_add(60 * 1000, fullscreen_saver_block_cb, fs);
323
324 /* hide normal window
325 * FIXME: properly restore this window on show
326 */
327 #ifdef HIDE_WINDOW_IN_FULLSCREEN
328 gtk_widget_hide(fs->normal_window);
329 #endif
330 image_change_path(fs->normal_imd, NULL, image_zoom_get(fs->normal_imd));
331
332 return fs;
333 }
334
335 void fullscreen_stop(FullScreenData *fs)
336 {
337 if (!fs) return;
338
339 g_source_remove(fs->saver_block_id);
340
341 fullscreen_hide_mouse_disable(fs);
342 fullscreen_busy_mouse_disable(fs);
343 gdk_keyboard_ungrab(GDK_CURRENT_TIME);
344
345 image_change_from_image(fs->normal_imd, fs->imd);
346 #ifdef HIDE_WINDOW_IN_FULLSCREEN
347 gtk_widget_show(fs->normal_window);
348 #endif
349 if (fs->stop_func) fs->stop_func(fs, fs->stop_data);
350
351 gtk_widget_destroy(fs->window);
352
353 g_free(fs);
354 }
355
356
357 /*
358 *----------------------------------------------------------------------------
359 * full screen preferences and utils
360 *----------------------------------------------------------------------------
361 */
362
363 GList *fullscreen_prefs_list(void)
364 {
365 GList *list = NULL;
366 GdkDisplay *display;
367 gint number;
368 gint i;
369
370 display = gdk_display_get_default();
371 number = gdk_display_get_n_screens(display);
372
373 for (i = 0; i < number ; i++)
374 {
375 GdkScreen *screen;
376 gint monitors;
377 gint j;
378
379 screen = gdk_display_get_screen(display, i);
380 monitors = gdk_screen_get_n_monitors(screen);
381
382 for (j = -1; j < monitors; j++)
383 {
384 ScreenData *sd;
385 GdkRectangle rect;
386 gchar *name;
387 gchar *subname;
388
389 name = gdk_screen_make_display_name(screen);
390
391 if (j < 0)
392 {
393 rect.x = 0;
394 rect.y = 0;
395 rect.width = gdk_screen_get_width(screen);
396 rect.height = gdk_screen_get_height(screen);
397 subname = g_strdup(_("Full size"));
398 }
399 else
400 {
401 gdk_screen_get_monitor_geometry(screen, j, &rect);
402 subname = g_strdup_printf("%s %d", _("Monitor"), j + 1);
403 }
404
405 sd = g_new0(ScreenData, 1);
406 sd->number = (i+1) * 100 + j + 1;
407 sd->description = g_strdup_printf("%s %s, %s", _("Screen"), name, subname);
408 sd->x = rect.x;
409 sd->y = rect.y;
410 sd->width = rect.width;
411 sd->height = rect.height;
412
413 if (debug) printf("Screen %d %30s %4d,%4d (%4dx%4d)\n",
414 sd->number, sd->description, sd->x, sd->y, sd->width, sd->height);
415
416 list = g_list_append(list, sd);
417
418 g_free(name);
419 g_free(subname);
420 }
421 }
422
423 return list;
424 }
425
426 void fullscreen_prefs_list_free(GList *list)
427 {
428 GList *work;
429
430 work = list;
431 while (work)
432 {
433 ScreenData *sd = work->data;
434 work = work->next;
435
436 g_free(sd->description);
437 g_free(sd);
438 }
439
440 g_list_free(list);
441 }
442
443 ScreenData *fullscreen_prefs_list_find(GList *list, gint screen)
444 {
445 GList *work;
446
447 work = list;
448 while (work)
449 {
450 ScreenData *sd = work->data;
451 work = work->next;
452
453 if (sd->number == screen) return sd;
454 }
455
456 return NULL;
457 }
458
459 /* screen is interpreted as such:
460 * -1 window manager determines size and position, fallback is (1) active monitor
461 * 0 full size of screen containing widget
462 * 1 size of monitor containing widget
463 * 100 full size of screen 1 (screen, monitor counts start at 1)
464 * 101 size of monitor 1 on screen 1
465 * 203 size of monitor 3 on screen 2
466 * returns:
467 * dest_screen: screen to place widget [use gtk_window_set_screen()]
468 * same_region: the returned region will overlap the current location of widget.
469 */
470 void fullscreen_prefs_get_geometry(gint screen, GtkWidget *widget, gint *x, gint *y, gint *width, gint *height,
471 GdkScreen **dest_screen, gint *same_region)
472 {
473 GList *list;
474 ScreenData *sd;
475
476 list = fullscreen_prefs_list();
477 if (screen >= 100)
478 {
479 sd = fullscreen_prefs_list_find(list, screen);
480 }
481 else
482 {
483 sd = NULL;
484 if (screen < 0) screen = 1;
485 }
486
487 if (sd)
488 {
489 GdkDisplay *display;
490 GdkScreen *screen;
491 gint n;
492
493 display = gdk_display_get_default();
494 n = sd->number / 100 - 1;
495 if (n >= 0 && n < gdk_display_get_n_screens(display))
496 {
497 screen = gdk_display_get_screen(display, n);
498 }
499 else
500 {
501 screen = gdk_display_get_default_screen(display);
502 }
503
504 if (x) *x = sd->x;
505 if (y) *y = sd->y;
506 if (width) *width = sd->width;
507 if (height) *height = sd->height;
508
509 if (dest_screen) *dest_screen = screen;
510 if (same_region) *same_region = (!widget || !widget->window ||
511 (screen == gtk_widget_get_screen(widget) &&
512 (sd->number%100 == 0 ||
513 sd->number%100 == gdk_screen_get_monitor_at_window(screen, widget->window)+1)));
514
515 }
516 else if (screen != 1 || !widget || !widget->window)
517 {
518 GdkScreen *screen;
519
520 if (widget)
521 {
522 screen = gtk_widget_get_screen(widget);
523 }
524 else
525 {
526 screen = gdk_screen_get_default();
527 }
528
529 if (x) *x = 0;
530 if (y) *y = 0;
531 if (width) *width = gdk_screen_get_width(screen);
532 if (height) *height = gdk_screen_get_height(screen);
533
534 if (dest_screen) *dest_screen = screen;
535 if (same_region) *same_region = TRUE;
536 }
537 else
538 {
539 GdkScreen *screen;
540 gint monitor;
541 GdkRectangle rect;
542
543 screen = gtk_widget_get_screen(widget);
544 monitor = gdk_screen_get_monitor_at_window(screen, widget->window);
545
546 gdk_screen_get_monitor_geometry(screen, monitor, &rect);
547
548 if (x) *x = rect.x;
549 if (y) *y = rect.y;
550 if (width) *width = rect.width;
551 if (height) *height = rect.height;
552
553 if (dest_screen) *dest_screen = screen;
554 if (same_region) *same_region = TRUE;
555 }
556
557 fullscreen_prefs_list_free(list);
558 }
559
560 gint fullscreen_prefs_find_screen_for_widget(GtkWidget *widget)
561 {
562 GdkScreen *screen;
563 gint monitor;
564 gint n;
565
566 if (!widget || !widget->window) return 0;
567
568 screen = gtk_widget_get_screen(widget);
569 monitor = gdk_screen_get_monitor_at_window(screen, widget->window);
570
571 n = (gdk_screen_get_number(screen)+1) * 100 + monitor + 1;
572
573 if (debug || TRUE) printf("Screen appears to be %d\n", n);
574
575 return n;
576 }
577
578 enum {
579 FS_MENU_COLUMN_NAME = 0,
580 FS_MENU_COLUMN_VALUE
581 };
582
583 #define BUTTON_ABOVE_KEY "button_above"
584
585 static void fullscreen_prefs_selection_cb(GtkWidget *combo, gpointer data)
586 {
587 gint *value = data;
588 GtkTreeModel *store;
589 GtkTreeIter iter;
590 GtkWidget *button;
591
592 if (!value) return;
593
594 store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
595 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return;
596 gtk_tree_model_get(store, &iter, FS_MENU_COLUMN_VALUE, value, -1);
597
598 button = g_object_get_data(G_OBJECT(combo), BUTTON_ABOVE_KEY);
599 if (button)
600 {
601 gtk_widget_set_sensitive(button, *value != -1);
602 }
603 }
604
605 static void fullscreen_prefs_selection_add(GtkListStore *store, const gchar *text, gint value)
606 {
607 GtkTreeIter iter;
608
609 gtk_list_store_append(store, &iter);
610 gtk_list_store_set(store, &iter, FS_MENU_COLUMN_NAME, text,
611 FS_MENU_COLUMN_VALUE, value, -1);
612 }
613
614 GtkWidget *fullscreen_prefs_selection_new(const gchar *text, gint *screen_value, gint *above_value)
615 {
616 GtkWidget *vbox;
617 GtkWidget *hbox;
618 GtkWidget *combo;
619 GtkListStore *store;
620 GtkCellRenderer *renderer;
621 GtkWidget *button = NULL;
622 GList *list;
623 GList *work;
624 gint current = 0;
625 gint n;
626
627 if (!screen_value) return NULL;
628
629 vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP);
630 hbox = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
631 if (text) pref_label_new(hbox, text);
632
633 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
634 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
635 g_object_unref(store);
636
637 renderer = gtk_cell_renderer_text_new();
638 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
639 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer,
640 "text", FS_MENU_COLUMN_NAME, NULL);
641
642 if (above_value)
643 {
644 button = pref_checkbox_new_int(vbox, _("Stay above other windows"),
645 *above_value, above_value);
646 gtk_widget_set_sensitive(button, *screen_value != -1);
647
648 g_object_set_data(G_OBJECT(combo), BUTTON_ABOVE_KEY, button);
649 }
650
651 fullscreen_prefs_selection_add(store, _("Determined by Window Manager"), -1);
652 fullscreen_prefs_selection_add(store, _("Active screen"), 0);
653 if (*screen_value == 0) current = 1;
654 fullscreen_prefs_selection_add(store, _("Active monitor"), 1);
655 if (*screen_value == 1) current = 2;
656
657 n = 3;
658 list = fullscreen_prefs_list();
659 work = list;
660 while (work)
661 {
662 ScreenData *sd = work->data;
663
664 fullscreen_prefs_selection_add(store, sd->description, sd->number);
665 if (*screen_value == sd->number) current = n;
666
667 work = work->next;
668 n++;
669 }
670 fullscreen_prefs_list_free(list);
671
672 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
673
674 gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
675 gtk_widget_show(combo);
676
677 g_signal_connect(G_OBJECT(combo), "changed",
678 G_CALLBACK(fullscreen_prefs_selection_cb), screen_value);
679
680 return vbox;
681 }
682