3
* Demonstrates migrating a window between different displays and
4
* screens. A display is a mouse and keyboard with some number of
5
* associated monitors. A screen is a set of monitors grouped
6
* into a single physical work area. The neat thing about having
7
* multiple displays is that they can be on a completely separate
8
* computers, as long as there is a network connection to the
9
* computer where the application is running.
11
* Only some of the windowing systems where GTK+ runs have the
12
* concept of multiple displays and screens. (The X Window System
13
* is the main example.) Other windowing systems can only
14
* handle one keyboard and mouse, and combine all monitors into
17
* This is a moderately complex example, and demonstrates:
19
* - Tracking the currently open displays and screens
21
* - Changing the screen for a window
23
* - Letting the user choose a window by clicking on it
25
* - Using GtkListStore and GtkTreeView
31
#include "demo-common.h"
33
/* The ChangeDisplayInfo structure corresponds to a toplevel window and
34
* holds pointers to widgets inside the toplevel window along with other
35
* information about the contents of the window.
36
* This is a common organizational structure in real applications.
38
typedef struct _ChangeDisplayInfo ChangeDisplayInfo;
40
struct _ChangeDisplayInfo
43
GtkSizeGroup *size_group;
45
GtkTreeModel *display_model;
46
GtkTreeModel *screen_model;
47
GtkTreeSelection *screen_selection;
49
GdkDisplay *current_display;
50
GdkScreen *current_screen;
53
/* These enumerations provide symbolic names for the columns
54
* in the two GtkListStore models.
59
DISPLAY_COLUMN_DISPLAY,
70
/* Finds the toplevel window under the mouse pointer, if any.
73
find_toplevel_at_pointer (GdkDisplay *display)
75
GdkWindow *pointer_window;
76
GtkWidget *widget = NULL;
78
pointer_window = gdk_display_get_window_at_pointer (display, NULL, NULL);
80
/* The user data field of a GdkWindow is used to store a pointer
81
* to the widget that created it.
84
gdk_window_get_user_data (pointer_window, (gpointer*) &widget);
86
return widget ? gtk_widget_get_toplevel (widget) : NULL;
90
button_release_event_cb (GtkWidget *widget,
91
GdkEventButton *event,
98
/* Asks the user to click on a window, then waits for them click
99
* the mouse. When the mouse is released, returns the toplevel
100
* window under the pointer, or NULL, if there is none.
103
query_for_toplevel (GdkScreen *screen,
106
GdkDisplay *display = gdk_screen_get_display (screen);
107
GtkWidget *popup, *label, *frame;
109
GtkWidget *toplevel = NULL;
111
popup = gtk_window_new (GTK_WINDOW_POPUP);
112
gtk_window_set_screen (GTK_WINDOW (popup), screen);
113
gtk_window_set_modal (GTK_WINDOW (popup), TRUE);
114
gtk_window_set_position (GTK_WINDOW (popup), GTK_WIN_POS_CENTER);
116
frame = gtk_frame_new (NULL);
117
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
118
gtk_container_add (GTK_CONTAINER (popup), frame);
120
label = gtk_label_new (prompt);
121
gtk_misc_set_padding (GTK_MISC (label), 10, 10);
122
gtk_container_add (GTK_CONTAINER (frame), label);
124
gtk_widget_show_all (popup);
125
cursor = gdk_cursor_new_for_display (display, GDK_CROSSHAIR);
127
if (gdk_pointer_grab (popup->window, FALSE,
128
GDK_BUTTON_RELEASE_MASK,
131
GDK_CURRENT_TIME) == GDK_GRAB_SUCCESS)
133
gboolean clicked = FALSE;
135
g_signal_connect (popup, "button-release-event",
136
G_CALLBACK (button_release_event_cb), &clicked);
138
/* Process events until clicked is set by button_release_event_cb.
139
* We pass in may_block=TRUE since we want to wait if there
140
* are no events currently.
143
g_main_context_iteration (NULL, TRUE);
145
toplevel = find_toplevel_at_pointer (gdk_screen_get_display (screen));
146
if (toplevel == popup)
150
gdk_cursor_unref (cursor);
151
gtk_widget_destroy (popup);
152
gdk_flush (); /* Really release the grab */
157
/* Prompts the user for a toplevel window to move, and then moves
158
* that window to the currently selected display
161
query_change_display (ChangeDisplayInfo *info)
163
GdkScreen *screen = gtk_widget_get_screen (info->window);
166
toplevel = query_for_toplevel (screen,
167
"Please select the toplevel\n"
168
"to move to the new screen");
171
gtk_window_set_screen (GTK_WINDOW (toplevel), info->current_screen);
173
gdk_display_beep (gdk_screen_get_display (screen));
176
/* Fills in the screen list based on the current display
179
fill_screens (ChangeDisplayInfo *info)
181
gtk_list_store_clear (GTK_LIST_STORE (info->screen_model));
183
if (info->current_display)
185
gint n_screens = gdk_display_get_n_screens (info->current_display);
188
for (i = 0; i < n_screens; i++)
190
GdkScreen *screen = gdk_display_get_screen (info->current_display, i);
193
gtk_list_store_append (GTK_LIST_STORE (info->screen_model), &iter);
194
gtk_list_store_set (GTK_LIST_STORE (info->screen_model), &iter,
195
SCREEN_COLUMN_NUMBER, i,
196
SCREEN_COLUMN_SCREEN, screen,
200
gtk_tree_selection_select_iter (info->screen_selection, &iter);
205
/* Called when the user clicks on a button in our dialog or
206
* closes the dialog through the window manager. Unless the
207
* "Change" button was clicked, we destroy the dialog.
210
response_cb (GtkDialog *dialog,
212
ChangeDisplayInfo *info)
214
if (response_id == GTK_RESPONSE_OK)
215
query_change_display (info);
217
gtk_widget_destroy (GTK_WIDGET (dialog));
220
/* Called when the user clicks on "Open..." in the display
221
* frame. Prompts for a new display, and then opens a connection
225
open_display_cb (GtkWidget *button,
226
ChangeDisplayInfo *info)
229
GtkWidget *display_entry;
230
GtkWidget *dialog_label;
231
gchar *new_screen_name = NULL;
232
GdkDisplay *result = NULL;
234
dialog = gtk_dialog_new_with_buttons ("Open Display",
235
GTK_WINDOW (info->window),
237
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
238
GTK_STOCK_OK, GTK_RESPONSE_OK,
241
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
242
display_entry = gtk_entry_new ();
243
gtk_entry_set_activates_default (GTK_ENTRY (display_entry), TRUE);
245
gtk_label_new ("Please enter the name of\nthe new display\n");
247
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), dialog_label);
248
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), display_entry);
250
gtk_widget_grab_focus (display_entry);
251
gtk_widget_show_all (GTK_BIN (dialog)->child);
255
gint response_id = gtk_dialog_run (GTK_DIALOG (dialog));
256
if (response_id != GTK_RESPONSE_OK)
259
new_screen_name = gtk_editable_get_chars (GTK_EDITABLE (display_entry),
262
if (strcmp (new_screen_name, "") != 0)
264
result = gdk_display_open (new_screen_name);
268
g_strdup_printf ("Can't open display :\n\t%s\nplease try another one\n",
270
gtk_label_set_text (GTK_LABEL (dialog_label), error_msg);
274
g_free (new_screen_name);
278
gtk_widget_destroy (dialog);
281
/* Called when the user clicks on the "Close" button in the
282
* "Display" frame. Closes the selected display.
285
close_display_cb (GtkWidget *button,
286
ChangeDisplayInfo *info)
288
if (info->current_display)
289
gdk_display_close (info->current_display);
292
/* Called when the selected row in the display list changes.
293
* Updates info->current_display, then refills the list of
297
display_changed_cb (GtkTreeSelection *selection,
298
ChangeDisplayInfo *info)
303
if (info->current_display)
304
g_object_unref (info->current_display);
305
if (gtk_tree_selection_get_selected (selection, &model, &iter))
306
gtk_tree_model_get (model, &iter,
307
DISPLAY_COLUMN_DISPLAY, &info->current_display,
310
info->current_display = NULL;
315
/* Called when the selected row in the sceen list changes.
316
* Updates info->current_screen.
319
screen_changed_cb (GtkTreeSelection *selection,
320
ChangeDisplayInfo *info)
325
if (info->current_screen)
326
g_object_unref (info->current_screen);
327
if (gtk_tree_selection_get_selected (selection, &model, &iter))
328
gtk_tree_model_get (model, &iter,
329
SCREEN_COLUMN_SCREEN, &info->current_screen,
332
info->current_screen = NULL;
335
/* This function is used both for creating the "Display" and
336
* "Screen" frames, since they have a similar structure. The
337
* caller hooks up the right context for the value returned
338
* in tree_view, and packs any relevant buttons into button_vbox.
341
create_frame (ChangeDisplayInfo *info,
344
GtkWidget **tree_view,
345
GtkWidget **button_vbox)
347
GtkTreeSelection *selection;
348
GtkWidget *scrollwin;
351
*frame = gtk_frame_new (title);
353
hbox = gtk_hbox_new (FALSE, 8);
354
gtk_container_set_border_width (GTK_CONTAINER (hbox), 8);
355
gtk_container_add (GTK_CONTAINER (*frame), hbox);
357
scrollwin = gtk_scrolled_window_new (NULL, NULL);
358
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin),
359
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
360
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin),
362
gtk_box_pack_start (GTK_BOX (hbox), scrollwin, TRUE, TRUE, 0);
364
*tree_view = gtk_tree_view_new ();
365
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (*tree_view), FALSE);
366
gtk_container_add (GTK_CONTAINER (scrollwin), *tree_view);
368
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (*tree_view));
369
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
371
*button_vbox = gtk_vbox_new (FALSE, 5);
372
gtk_box_pack_start (GTK_BOX (hbox), *button_vbox, FALSE, FALSE, 0);
374
if (!info->size_group)
375
info->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
377
gtk_size_group_add_widget (GTK_SIZE_GROUP (info->size_group), *button_vbox);
380
/* If we have a stack of buttons, it often looks better if their contents
381
* are left-aligned, rather than centered. This function creates a button
382
* and left-aligns it contents.
385
left_align_button_new (const char *label)
387
GtkWidget *button = gtk_button_new_with_mnemonic (label);
388
GtkWidget *child = gtk_bin_get_child (GTK_BIN (button));
390
gtk_misc_set_alignment (GTK_MISC (child), 0., 0.5);
395
/* Creates the "Display" frame in the main window.
398
create_display_frame (ChangeDisplayInfo *info)
401
GtkWidget *tree_view;
402
GtkWidget *button_vbox;
403
GtkTreeViewColumn *column;
404
GtkTreeSelection *selection;
407
create_frame (info, "Display", &frame, &tree_view, &button_vbox);
409
button = left_align_button_new ("_Open...");
410
g_signal_connect (button, "clicked", G_CALLBACK (open_display_cb), info);
411
gtk_box_pack_start (GTK_BOX (button_vbox), button, FALSE, FALSE, 0);
413
button = left_align_button_new ("_Close");
414
g_signal_connect (button, "clicked", G_CALLBACK (close_display_cb), info);
415
gtk_box_pack_start (GTK_BOX (button_vbox), button, FALSE, FALSE, 0);
417
info->display_model = (GtkTreeModel *)gtk_list_store_new (DISPLAY_NUM_COLUMNS,
421
gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), info->display_model);
423
column = gtk_tree_view_column_new_with_attributes ("Name",
424
gtk_cell_renderer_text_new (),
425
"text", DISPLAY_COLUMN_NAME,
427
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
429
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
430
g_signal_connect (selection, "changed",
431
G_CALLBACK (display_changed_cb), info);
436
/* Creates the "Screen" frame in the main window.
439
create_screen_frame (ChangeDisplayInfo *info)
442
GtkWidget *tree_view;
443
GtkWidget *button_vbox;
444
GtkTreeViewColumn *column;
446
create_frame (info, "Screen", &frame, &tree_view, &button_vbox);
448
info->screen_model = (GtkTreeModel *)gtk_list_store_new (SCREEN_NUM_COLUMNS,
452
gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), info->screen_model);
454
column = gtk_tree_view_column_new_with_attributes ("Number",
455
gtk_cell_renderer_text_new (),
456
"text", SCREEN_COLUMN_NUMBER,
458
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
460
info->screen_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
461
g_signal_connect (info->screen_selection, "changed",
462
G_CALLBACK (screen_changed_cb), info);
467
/* Called when one of the currently open displays is closed.
468
* Remove it from our list of displays.
471
display_closed_cb (GdkDisplay *display,
473
ChangeDisplayInfo *info)
478
for (valid = gtk_tree_model_get_iter_first (info->display_model, &iter);
480
valid = gtk_tree_model_iter_next (info->display_model, &iter))
482
GdkDisplay *tmp_display;
484
gtk_tree_model_get (info->display_model, &iter,
485
DISPLAY_COLUMN_DISPLAY, &tmp_display,
487
if (tmp_display == display)
489
gtk_list_store_remove (GTK_LIST_STORE (info->display_model), &iter);
495
/* Adds a new display to our list of displays, and connects
496
* to the "closed" signal so that we can remove it from the
497
* list of displays again.
500
add_display (ChangeDisplayInfo *info,
503
const gchar *name = gdk_display_get_name (display);
506
gtk_list_store_append (GTK_LIST_STORE (info->display_model), &iter);
507
gtk_list_store_set (GTK_LIST_STORE (info->display_model), &iter,
508
DISPLAY_COLUMN_NAME, name,
509
DISPLAY_COLUMN_DISPLAY, display,
512
g_signal_connect (display, "closed",
513
G_CALLBACK (display_closed_cb), info);
516
/* Called when a new display is opened
519
display_opened_cb (GdkDisplayManager *manager,
521
ChangeDisplayInfo *info)
523
add_display (info, display);
526
/* Adds all currently open displays to our list of displays,
527
* and set up a signal connection so that we'll be notified
528
* when displays are opened in the future as well.
531
initialize_displays (ChangeDisplayInfo *info)
533
GdkDisplayManager *manager = gdk_display_manager_get ();
534
GSList *displays = gdk_display_manager_list_displays (manager);
537
for (tmp_list = displays; tmp_list; tmp_list = tmp_list->next)
538
add_display (info, tmp_list->data);
540
g_slist_free (tmp_list);
542
g_signal_connect (manager, "display_opened",
543
G_CALLBACK (display_opened_cb), info);
546
/* Cleans up when the toplevel is destroyed; we remove the
547
* connections we use to track currently open displays, then
548
* free the ChangeDisplayInfo structure.
551
destroy_info (ChangeDisplayInfo *info)
553
GdkDisplayManager *manager = gdk_display_manager_get ();
554
GSList *displays = gdk_display_manager_list_displays (manager);
557
g_signal_handlers_disconnect_by_func (manager,
561
for (tmp_list = displays; tmp_list; tmp_list = tmp_list->next)
562
g_signal_handlers_disconnect_by_func (tmp_list->data,
566
g_slist_free (tmp_list);
568
g_object_unref (info->size_group);
569
g_object_unref (info->display_model);
570
g_object_unref (info->screen_model);
572
if (info->current_display)
573
g_object_unref (info->current_display);
574
if (info->current_screen)
575
g_object_unref (info->current_screen);
581
destroy_cb (GtkObject *object,
582
ChangeDisplayInfo **info)
584
destroy_info (*info);
588
/* Main entry point. If the dialog for this demo doesn't yet exist, creates
589
* it. Otherwise, destroys it.
592
do_changedisplay (GtkWidget *do_widget)
594
static ChangeDisplayInfo *info = NULL;
601
info = g_new0 (ChangeDisplayInfo, 1);
603
info->window = gtk_dialog_new_with_buttons ("Change Screen or display",
604
GTK_WINDOW (do_widget),
605
GTK_DIALOG_NO_SEPARATOR,
606
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
607
"Change", GTK_RESPONSE_OK,
610
gtk_window_set_default_size (GTK_WINDOW (info->window), 300, 400);
612
g_signal_connect (info->window, "response",
613
G_CALLBACK (response_cb), info);
614
g_signal_connect (info->window, "destroy",
615
G_CALLBACK (destroy_cb), &info);
617
vbox = gtk_vbox_new (FALSE, 5);
618
gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
620
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (info->window)->vbox), vbox,
623
frame = create_display_frame (info);
624
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
626
frame = create_screen_frame (info);
627
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
629
initialize_displays (info);
631
gtk_widget_show_all (info->window);
636
gtk_widget_destroy (info->window);