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 #undef GDK_DISABLE_DEPRECATED
34 #include "demo-common.h"
36 /* The ChangeDisplayInfo structure corresponds to a toplevel window and
37 * holds pointers to widgets inside the toplevel window along with other
38 * information about the contents of the window.
39 * This is a common organizational structure in real applications.
41 typedef struct _ChangeDisplayInfo ChangeDisplayInfo;
43 struct _ChangeDisplayInfo
46 GtkSizeGroup *size_group;
48 GtkTreeModel *display_model;
49 GtkTreeModel *screen_model;
50 GtkTreeSelection *screen_selection;
52 GdkDisplay *current_display;
53 GdkScreen *current_screen;
56 /* These enumerations provide symbolic names for the columns
57 * in the two GtkListStore models.
62 DISPLAY_COLUMN_DISPLAY,
73 /* Finds the toplevel window under the mouse pointer, if any.
76 find_toplevel_at_pointer (GdkDisplay *display)
78 GdkWindow *pointer_window;
79 GtkWidget *widget = NULL;
81 pointer_window = gdk_device_get_window_at_position (gtk_get_current_event_device (),
84 /* The user data field of a GdkWindow is used to store a pointer
85 * to the widget that created it.
90 gdk_window_get_user_data (pointer_window, &widget_ptr);
94 return widget ? gtk_widget_get_toplevel (widget) : NULL;
98 button_release_event_cb (GtkWidget *widget,
99 GdkEventButton *event,
106 /* Asks the user to click on a window, then waits for them click
107 * the mouse. When the mouse is released, returns the toplevel
108 * window under the pointer, or NULL, if there is none.
111 query_for_toplevel (GdkScreen *screen,
114 GdkDisplay *display = gdk_screen_get_display (screen);
115 GtkWidget *popup, *label, *frame;
117 GtkWidget *toplevel = NULL;
119 popup = gtk_window_new (GTK_WINDOW_POPUP);
120 gtk_window_set_screen (GTK_WINDOW (popup), screen);
121 gtk_window_set_modal (GTK_WINDOW (popup), TRUE);
122 gtk_window_set_position (GTK_WINDOW (popup), GTK_WIN_POS_CENTER);
124 frame = gtk_frame_new (NULL);
125 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
126 gtk_container_add (GTK_CONTAINER (popup), frame);
128 label = gtk_label_new (prompt);
129 g_object_set (label, "margin", 10, NULL);
130 gtk_container_add (GTK_CONTAINER (frame), label);
132 gtk_widget_show_all (popup);
133 cursor = gdk_cursor_new_for_display (display, GDK_CROSSHAIR);
135 if (gdk_device_grab (gtk_get_current_event_device (),
136 gtk_widget_get_window (popup),
139 GDK_BUTTON_RELEASE_MASK,
141 GDK_CURRENT_TIME) == GDK_GRAB_SUCCESS)
143 gboolean clicked = FALSE;
145 g_signal_connect (popup, "button-release-event",
146 G_CALLBACK (button_release_event_cb), &clicked);
148 /* Process events until clicked is set by button_release_event_cb.
149 * We pass in may_block=TRUE since we want to wait if there
150 * are no events currently.
153 g_main_context_iteration (NULL, TRUE);
155 toplevel = find_toplevel_at_pointer (gdk_screen_get_display (screen));
156 if (toplevel == popup)
160 g_object_unref (cursor);
161 gtk_widget_destroy (popup);
162 gdk_flush (); /* Really release the grab */
167 /* Prompts the user for a toplevel window to move, and then moves
168 * that window to the currently selected display
171 query_change_display (ChangeDisplayInfo *info)
173 GdkScreen *screen = gtk_widget_get_screen (info->window);
176 toplevel = query_for_toplevel (screen,
177 "Please select the toplevel\n"
178 "to move to the new screen");
181 gtk_window_set_screen (GTK_WINDOW (toplevel), info->current_screen);
183 gdk_display_beep (gdk_screen_get_display (screen));
186 /* Fills in the screen list based on the current display
189 fill_screens (ChangeDisplayInfo *info)
191 gtk_list_store_clear (GTK_LIST_STORE (info->screen_model));
193 if (info->current_display)
195 gint n_screens = gdk_display_get_n_screens (info->current_display);
198 for (i = 0; i < n_screens; i++)
200 GdkScreen *screen = gdk_display_get_screen (info->current_display, i);
203 gtk_list_store_append (GTK_LIST_STORE (info->screen_model), &iter);
204 gtk_list_store_set (GTK_LIST_STORE (info->screen_model), &iter,
205 SCREEN_COLUMN_NUMBER, i,
206 SCREEN_COLUMN_SCREEN, screen,
210 gtk_tree_selection_select_iter (info->screen_selection, &iter);
215 /* Called when the user clicks on a button in our dialog or
216 * closes the dialog through the window manager. Unless the
217 * "Change" button was clicked, we destroy the dialog.
220 response_cb (GtkDialog *dialog,
222 ChangeDisplayInfo *info)
224 if (response_id == GTK_RESPONSE_OK)
225 query_change_display (info);
227 gtk_widget_destroy (GTK_WIDGET (dialog));
230 /* Called when the user clicks on "Open..." in the display
231 * frame. Prompts for a new display, and then opens a connection
235 open_display_cb (GtkWidget *button,
236 ChangeDisplayInfo *info)
238 GtkWidget *content_area;
240 GtkWidget *display_entry;
241 GtkWidget *dialog_label;
242 gchar *new_screen_name = NULL;
243 GdkDisplay *result = NULL;
245 dialog = gtk_dialog_new_with_buttons ("Open Display",
246 GTK_WINDOW (info->window),
248 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
249 GTK_STOCK_OK, GTK_RESPONSE_OK,
252 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
253 display_entry = gtk_entry_new ();
254 gtk_entry_set_activates_default (GTK_ENTRY (display_entry), TRUE);
256 gtk_label_new ("Please enter the name of\nthe new display\n");
258 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
260 gtk_container_add (GTK_CONTAINER (content_area), dialog_label);
261 gtk_container_add (GTK_CONTAINER (content_area), display_entry);
263 gtk_widget_grab_focus (display_entry);
264 gtk_widget_show_all (gtk_bin_get_child (GTK_BIN (dialog)));
268 gint response_id = gtk_dialog_run (GTK_DIALOG (dialog));
269 if (response_id != GTK_RESPONSE_OK)
272 new_screen_name = gtk_editable_get_chars (GTK_EDITABLE (display_entry),
275 if (strcmp (new_screen_name, "") != 0)
277 result = gdk_display_open (new_screen_name);
281 g_strdup_printf ("Can't open display :\n\t%s\nplease try another one\n",
283 gtk_label_set_text (GTK_LABEL (dialog_label), error_msg);
287 g_free (new_screen_name);
291 gtk_widget_destroy (dialog);
294 /* Called when the user clicks on the "Close" button in the
295 * "Display" frame. Closes the selected display.
298 close_display_cb (GtkWidget *button,
299 ChangeDisplayInfo *info)
301 if (info->current_display)
302 gdk_display_close (info->current_display);
305 /* Called when the selected row in the display list changes.
306 * Updates info->current_display, then refills the list of
310 display_changed_cb (GtkTreeSelection *selection,
311 ChangeDisplayInfo *info)
316 if (info->current_display)
317 g_object_unref (info->current_display);
318 if (gtk_tree_selection_get_selected (selection, &model, &iter))
319 gtk_tree_model_get (model, &iter,
320 DISPLAY_COLUMN_DISPLAY, &info->current_display,
323 info->current_display = NULL;
328 /* Called when the selected row in the sceen list changes.
329 * Updates info->current_screen.
332 screen_changed_cb (GtkTreeSelection *selection,
333 ChangeDisplayInfo *info)
338 if (info->current_screen)
339 g_object_unref (info->current_screen);
340 if (gtk_tree_selection_get_selected (selection, &model, &iter))
341 gtk_tree_model_get (model, &iter,
342 SCREEN_COLUMN_SCREEN, &info->current_screen,
345 info->current_screen = NULL;
348 /* This function is used both for creating the "Display" and
349 * "Screen" frames, since they have a similar structure. The
350 * caller hooks up the right context for the value returned
351 * in tree_view, and packs any relevant buttons into button_vbox.
354 create_frame (ChangeDisplayInfo *info,
357 GtkWidget **tree_view,
358 GtkWidget **button_vbox)
360 GtkTreeSelection *selection;
361 GtkWidget *scrollwin;
364 *frame = gtk_frame_new (title);
366 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
367 gtk_container_set_border_width (GTK_CONTAINER (hbox), 8);
368 gtk_container_add (GTK_CONTAINER (*frame), hbox);
370 scrollwin = gtk_scrolled_window_new (NULL, NULL);
371 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin),
372 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
373 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin),
375 gtk_box_pack_start (GTK_BOX (hbox), scrollwin, TRUE, TRUE, 0);
377 *tree_view = gtk_tree_view_new ();
378 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (*tree_view), FALSE);
379 gtk_container_add (GTK_CONTAINER (scrollwin), *tree_view);
381 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (*tree_view));
382 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
384 *button_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
385 gtk_box_pack_start (GTK_BOX (hbox), *button_vbox, FALSE, FALSE, 0);
387 if (!info->size_group)
388 info->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
390 gtk_size_group_add_widget (GTK_SIZE_GROUP (info->size_group), *button_vbox);
393 /* If we have a stack of buttons, it often looks better if their contents
394 * are left-aligned, rather than centered. This function creates a button
395 * and left-aligns it contents.
398 left_align_button_new (const char *label)
400 GtkWidget *button = gtk_button_new_with_mnemonic (label);
401 GtkWidget *child = gtk_bin_get_child (GTK_BIN (button));
403 gtk_widget_set_halign (child, GTK_ALIGN_START);
404 gtk_widget_set_valign (child, GTK_ALIGN_CENTER);
409 /* Creates the "Display" frame in the main window.
412 create_display_frame (ChangeDisplayInfo *info)
415 GtkWidget *tree_view;
416 GtkWidget *button_vbox;
417 GtkTreeViewColumn *column;
418 GtkTreeSelection *selection;
421 create_frame (info, "Display", &frame, &tree_view, &button_vbox);
423 button = left_align_button_new ("_Open...");
424 g_signal_connect (button, "clicked", G_CALLBACK (open_display_cb), info);
425 gtk_box_pack_start (GTK_BOX (button_vbox), button, FALSE, FALSE, 0);
427 button = left_align_button_new ("_Close");
428 g_signal_connect (button, "clicked", G_CALLBACK (close_display_cb), info);
429 gtk_box_pack_start (GTK_BOX (button_vbox), button, FALSE, FALSE, 0);
431 info->display_model = (GtkTreeModel *)gtk_list_store_new (DISPLAY_NUM_COLUMNS,
435 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), info->display_model);
437 column = gtk_tree_view_column_new_with_attributes ("Name",
438 gtk_cell_renderer_text_new (),
439 "text", DISPLAY_COLUMN_NAME,
441 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
443 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
444 g_signal_connect (selection, "changed",
445 G_CALLBACK (display_changed_cb), info);
450 /* Creates the "Screen" frame in the main window.
453 create_screen_frame (ChangeDisplayInfo *info)
456 GtkWidget *tree_view;
457 GtkWidget *button_vbox;
458 GtkTreeViewColumn *column;
460 create_frame (info, "Screen", &frame, &tree_view, &button_vbox);
462 info->screen_model = (GtkTreeModel *)gtk_list_store_new (SCREEN_NUM_COLUMNS,
466 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), info->screen_model);
468 column = gtk_tree_view_column_new_with_attributes ("Number",
469 gtk_cell_renderer_text_new (),
470 "text", SCREEN_COLUMN_NUMBER,
472 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
474 info->screen_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
475 g_signal_connect (info->screen_selection, "changed",
476 G_CALLBACK (screen_changed_cb), info);
481 /* Called when one of the currently open displays is closed.
482 * Remove it from our list of displays.
485 display_closed_cb (GdkDisplay *display,
487 ChangeDisplayInfo *info)
492 for (valid = gtk_tree_model_get_iter_first (info->display_model, &iter);
494 valid = gtk_tree_model_iter_next (info->display_model, &iter))
496 GdkDisplay *tmp_display;
498 gtk_tree_model_get (info->display_model, &iter,
499 DISPLAY_COLUMN_DISPLAY, &tmp_display,
501 if (tmp_display == display)
503 gtk_list_store_remove (GTK_LIST_STORE (info->display_model), &iter);
509 /* Adds a new display to our list of displays, and connects
510 * to the "closed" signal so that we can remove it from the
511 * list of displays again.
514 add_display (ChangeDisplayInfo *info,
517 const gchar *name = gdk_display_get_name (display);
520 gtk_list_store_append (GTK_LIST_STORE (info->display_model), &iter);
521 gtk_list_store_set (GTK_LIST_STORE (info->display_model), &iter,
522 DISPLAY_COLUMN_NAME, name,
523 DISPLAY_COLUMN_DISPLAY, display,
526 g_signal_connect (display, "closed",
527 G_CALLBACK (display_closed_cb), info);
530 /* Called when a new display is opened
533 display_opened_cb (GdkDisplayManager *manager,
535 ChangeDisplayInfo *info)
537 add_display (info, display);
540 /* Adds all currently open displays to our list of displays,
541 * and set up a signal connection so that we'll be notified
542 * when displays are opened in the future as well.
545 initialize_displays (ChangeDisplayInfo *info)
547 GdkDisplayManager *manager = gdk_display_manager_get ();
548 GSList *displays = gdk_display_manager_list_displays (manager);
551 for (tmp_list = displays; tmp_list; tmp_list = tmp_list->next)
552 add_display (info, tmp_list->data);
554 g_slist_free (tmp_list);
556 g_signal_connect (manager, "display-opened",
557 G_CALLBACK (display_opened_cb), info);
560 /* Cleans up when the toplevel is destroyed; we remove the
561 * connections we use to track currently open displays, then
562 * free the ChangeDisplayInfo structure.
565 destroy_info (ChangeDisplayInfo *info)
567 GdkDisplayManager *manager = gdk_display_manager_get ();
568 GSList *displays = gdk_display_manager_list_displays (manager);
571 g_signal_handlers_disconnect_by_func (manager,
575 for (tmp_list = displays; tmp_list; tmp_list = tmp_list->next)
576 g_signal_handlers_disconnect_by_func (tmp_list->data,
580 g_slist_free (tmp_list);
582 g_object_unref (info->size_group);
583 g_object_unref (info->display_model);
584 g_object_unref (info->screen_model);
586 if (info->current_display)
587 g_object_unref (info->current_display);
588 if (info->current_screen)
589 g_object_unref (info->current_screen);
595 destroy_cb (GObject *object,
596 ChangeDisplayInfo **info)
598 destroy_info (*info);
602 /* Main entry point. If the dialog for this demo doesn't yet exist, creates
603 * it. Otherwise, destroys it.
606 do_changedisplay (GtkWidget *do_widget)
608 static ChangeDisplayInfo *info = NULL;
612 GtkWidget *content_area;
616 info = g_new0 (ChangeDisplayInfo, 1);
618 info->window = gtk_dialog_new_with_buttons ("Change Screen or display",
619 GTK_WINDOW (do_widget),
621 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
622 "Change", GTK_RESPONSE_OK,
625 gtk_window_set_default_size (GTK_WINDOW (info->window), 300, 400);
627 g_signal_connect (info->window, "response",
628 G_CALLBACK (response_cb), info);
629 g_signal_connect (info->window, "destroy",
630 G_CALLBACK (destroy_cb), &info);
632 content_area = gtk_dialog_get_content_area (GTK_DIALOG (info->window));
634 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
635 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
636 gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
638 frame = create_display_frame (info);
639 gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
641 frame = create_screen_frame (info);
642 gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
644 initialize_displays (info);
646 gtk_widget_show_all (info->window);
651 gtk_widget_destroy (info->window);