2009-08-26 Chris Toshok <toshok@ximian.com>
[moon.git] / src / window-gtk.cpp
blobe438f9b896777a61b0034db352a720c5d62f15bc
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * window-gtk.cpp: MoonWindow implementation using gtk widgets.
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007-2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include "window-gtk.h"
15 #include "deployment.h"
16 #include "timemanager.h"
18 MoonWindowGtk::MoonWindowGtk (bool fullscreen, int w, int h, MoonWindow *parent)
19 : MoonWindow (w, h)
21 this->fullscreen = fullscreen;
22 this->deployment = Deployment::GetCurrent ();
24 if (fullscreen)
25 InitializeFullScreen(parent);
26 else
27 InitializeNormal();
30 MoonWindowGtk::~MoonWindowGtk ()
32 DisableEvents ();
33 if (widget != NULL)
34 gtk_widget_destroy (widget);
37 GdkWindow *
38 MoonWindowGtk::GetGdkWindow ()
40 GdkWindow *parent_window = gtk_widget_get_parent_window (widget);
41 if (parent_window == NULL)
42 parent_window = widget->window;
44 g_object_ref (parent_window);
45 return parent_window;
48 void
49 MoonWindowGtk::InitializeFullScreen (MoonWindow *parent)
51 widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
53 // only fullscreen on the monitor the plugin is on
54 GdkWindow *gdk = parent->GetGdkWindow ();
55 int monitor = gdk_screen_get_monitor_at_window (gdk_screen_get_default (), gdk);
56 GdkRectangle bounds;
57 gdk_screen_get_monitor_geometry (gdk_screen_get_default (), monitor, &bounds);
58 width = bounds.width;
59 height = bounds.height;
60 gtk_window_move (GTK_WINDOW (widget), bounds.x, bounds.y);
62 gtk_window_fullscreen (GTK_WINDOW (widget));
64 InitializeCommon ();
66 Show();
68 g_object_unref (gdk);
71 void
72 MoonWindowGtk::InitializeNormal ()
74 if (width == -1 || height == -1) {
75 g_warning ("you must specify width and height when creating a non-fullscreen gtk window");
76 width = 0;
77 height = 0;
80 widget = gtk_event_box_new ();
82 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget), false);
84 InitializeCommon ();
86 Show ();
89 void
90 MoonWindowGtk::InitializeCommon ()
92 // don't let gtk clear the window we'll do all the drawing.
93 //gtk_widget_set_app_paintable (widget, true);
94 gtk_widget_set_double_buffered (widget, false);
95 gtk_widget_set_size_request (widget, width, height);
97 g_signal_connect (widget, "size-allocate", G_CALLBACK (widget_size_allocate), this);
98 g_signal_connect (widget, "destroy", G_CALLBACK (widget_destroyed), this);
100 gtk_widget_add_events (widget,
101 GDK_POINTER_MOTION_MASK |
102 //GDK_POINTER_MOTION_HINT_MASK |
103 GDK_KEY_PRESS_MASK |
104 GDK_KEY_RELEASE_MASK |
105 GDK_BUTTON_PRESS_MASK |
106 GDK_BUTTON_RELEASE_MASK |
107 ((moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) != 0 ? GDK_SCROLL_MASK : 0) |
108 GDK_FOCUS_CHANGE_MASK);
110 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
113 void
114 MoonWindowGtk::Resize (int width, int height)
116 gtk_widget_set_size_request (widget, width, height);
117 gtk_widget_queue_resize (widget);
120 /* XPM */
121 static const char *dot[] = {
122 "18 18 4 1",
123 " c None",
124 ". c #808080",
125 "+ c #303030",
126 "@ c #000000",
127 ".+. ",
128 "@@@ ",
129 ".@. ",
130 " ",
131 " ",
132 " ",
133 " ",
134 " ",
135 " ",
136 " ",
137 " ",
138 " ",
139 " ",
140 " ",
141 " ",
142 " ",
143 " ",
147 /* XPM */
148 static const char *eraser[] = {
149 "20 20 49 1",
150 " c None",
151 ". c #000000",
152 "+ c #858585",
153 "@ c #E8E8E8",
154 "# c #E9E9E9",
155 "$ c #E7E7E7",
156 "% c #E2E2E2",
157 "& c #D6D6D6",
158 "* c #7D7D7D",
159 "= c #565656",
160 "- c #E1E1E1",
161 "; c #E0E0E0",
162 "> c #DEDEDE",
163 ", c #DFDFDF",
164 "' c #474747",
165 ") c #6C6C6C",
166 "! c #B0B0B0",
167 "~ c #E3E3E3",
168 "{ c #4E4E4E",
169 "] c #636363",
170 "^ c #E6E6E6",
171 "/ c #505050",
172 "( c #4A4A4A",
173 "_ c #C7C7C7",
174 ": c #272727",
175 "< c #797979",
176 "[ c #E5E5E5",
177 "} c #DDDDDD",
178 "| c #9C9C9C",
179 "1 c #232323",
180 "2 c #E4E4E4",
181 "3 c #656565",
182 "4 c #313131",
183 "5 c #EAEAEA",
184 "6 c #ECECEC",
185 "7 c #EEEEEE",
186 "8 c #EFEFEF",
187 "9 c #F0F0F0",
188 "0 c #999999",
189 "a c #5D5D5D",
190 "b c #343434",
191 "c c #757575",
192 "d c #383838",
193 "e c #CECECE",
194 "f c #A9A9A9",
195 "g c #6F6F6F",
196 "h c #B3B3B3",
197 "i c #787878",
198 "j c #3F3F3F",
199 " ",
200 " ",
201 " ",
202 " ",
203 " ",
204 " ",
205 " ........... ",
206 " .+@#@@@$$%&*. ",
207 " =-;%>>>>>>>,' ",
208 " )!~>>>>>>>>>>{ ",
209 " ]^>>>>>>>>>,,/ ",
210 " (_;>>>>>>>>,>&: ",
211 " <[,}>>>>>>>-,| ",
212 " 1[-;>>>>>>>$2,3 ",
213 " 45678999998550a ",
214 " b~,,,,,,,,,;$c ",
215 " de-,,,,,,,,,fg ",
216 " bh%%,,;}}}>>ij ",
217 " ............ ",
221 void
222 MoonWindowGtk::SetBackgroundColor (Color *color)
224 GdkColor gdk_color;
225 gdk_color.red = color->r * 0xffff;
226 gdk_color.green = color->g * 0xffff;
227 gdk_color.blue = color->b * 0xffff;
229 gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, &gdk_color);
231 MoonWindow::SetBackgroundColor (color);
234 void
235 MoonWindowGtk::SetCursor (MouseCursor cursor)
237 if (widget->window) {
239 GdkCursor *c = NULL;
240 switch (cursor) {
241 case MouseCursorDefault:
242 c = NULL;
243 break;
244 case MouseCursorArrow:
245 c = gdk_cursor_new (GDK_LEFT_PTR);
246 break;
247 case MouseCursorHand:
248 c = gdk_cursor_new (GDK_HAND2);
249 break;
250 case MouseCursorWait:
251 c = gdk_cursor_new (GDK_WATCH);
252 break;
253 case MouseCursorIBeam:
254 c = gdk_cursor_new (GDK_XTERM);
255 break;
256 case MouseCursorStylus:
257 c = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), gdk_pixbuf_new_from_xpm_data ((const char**) dot), 0, 0);
258 break;
259 case MouseCursorEraser:
260 c = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), gdk_pixbuf_new_from_xpm_data ((const char**) eraser), 8, 8);
261 break;
262 case MouseCursorSizeNS:
263 c = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
264 break;
265 case MouseCursorSizeWE:
266 c = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
267 break;
268 case MouseCursorNone:
269 // Silverlight display no cursor if the enumeration value is invalid (e.g. -1)
270 default:
271 //from gdk-cursor doc :"To make the cursor invisible, use gdk_cursor_new_from_pixmap() to create a cursor with no pixels in it."
272 GdkPixmap *empty = gdk_bitmap_create_from_data (NULL, "0x00", 1, 1);
273 GdkColor empty_color = {0, 0, 0, 0};
274 c = gdk_cursor_new_from_pixmap (empty, empty, &empty_color, &empty_color, 0, 0);
275 g_object_unref (empty);
276 break;
280 gdk_window_set_cursor (widget->window, c);
282 if (c)
283 gdk_cursor_unref (c);
287 void
288 MoonWindowGtk::Invalidate (Rect r)
290 gtk_widget_queue_draw_area (widget,
291 (int) (widget->allocation.x + r.x),
292 (int) (widget->allocation.y + r.y),
293 (int) r.width, (int)r.height);
296 void
297 MoonWindowGtk::ProcessUpdates ()
299 if (widget->window)
300 gdk_window_process_updates (widget->window, false);
303 gboolean
304 MoonWindowGtk::HandleEvent (XEvent *event)
306 // nothing to do here, since we don't pump events into the gtk
307 // window, gtk calls our signal handlers directly.
308 return TRUE;
311 void
312 MoonWindowGtk::Show ()
314 gtk_widget_show (widget);
316 // The window has to be realized for this call to work
317 gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_CURSOR);
318 /* we need to explicitly enable the devices */
319 for (GList *l = gdk_devices_list(); l; l = l->next) {
320 #if THIS_NOLONGER_BREAKS_LARRYS_MOUSE
321 GdkDevice *device = GDK_DEVICE(l->data);
322 //if (!device->has_cursor)
323 gdk_device_set_mode (device, GDK_MODE_SCREEN);
324 #endif
327 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
330 void
331 MoonWindowGtk::Hide ()
333 gtk_widget_hide (widget);
336 void
337 MoonWindowGtk::EnableEvents (bool first)
339 g_signal_connect (widget, "expose-event", G_CALLBACK (expose_event), this);
340 g_signal_connect (widget, "motion-notify-event", G_CALLBACK (motion_notify), this);
341 g_signal_connect (widget, "enter-notify-event", G_CALLBACK (crossing_notify), this);
342 g_signal_connect (widget, "leave-notify-event", G_CALLBACK (crossing_notify), this);
343 g_signal_connect (widget, "key-press-event", G_CALLBACK (key_press), this);
344 g_signal_connect (widget, "key-release-event", G_CALLBACK (key_release), this);
345 g_signal_connect (widget, "button-press-event", G_CALLBACK (button_press), this);
346 g_signal_connect (widget, "button-release-event", G_CALLBACK (button_release), this);
347 if ((moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) != 0)
348 g_signal_connect (widget, "scroll-event", G_CALLBACK (scroll), this);
349 g_signal_connect (widget, "focus-in-event", G_CALLBACK (focus_in), this);
350 g_signal_connect (widget, "focus-out-event", G_CALLBACK (focus_out), this);
352 if (first) {
353 g_signal_connect (widget, "realize", G_CALLBACK (realized), this);
354 g_signal_connect (widget, "unrealize", G_CALLBACK (unrealized), this);
356 if (GTK_WIDGET_REALIZED (widget))
357 realized (widget, this);
361 void
362 MoonWindowGtk::DisableEvents ()
364 g_signal_handlers_disconnect_matched (widget, G_SIGNAL_MATCH_DATA,
365 0, 0, NULL, NULL, this);
368 void
369 MoonWindowGtk::GrabFocus ()
371 gtk_widget_grab_focus (widget);
374 bool
375 MoonWindowGtk::HasFocus ()
377 return GTK_WIDGET_HAS_FOCUS (widget);
380 gboolean
381 MoonWindowGtk::expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data)
383 MoonWindowGtk *window = (MoonWindowGtk*)data;
385 Deployment::SetCurrent (window->GetDeployment ());
387 if (!window->surface)
388 return true;
390 // we draw to a backbuffer pixmap, then transfer the contents
391 // to the widget's window.
392 GdkPixmap *pixmap = gdk_pixmap_new (widget->window,
393 MAX (event->area.width, 1), MAX (event->area.height, 1), -1);
395 window->surface->PaintToDrawable (pixmap,
396 gdk_drawable_get_visual (widget->window),
397 event,
398 widget->allocation.x,
399 widget->allocation.y,
400 window->GetTransparent (),
401 true);
403 GdkGC *gc = gdk_gc_new (pixmap);
405 gdk_gc_set_clip_region (gc, event->region);
407 gdk_draw_drawable (widget->window, gc, pixmap,
408 0, 0,
409 event->area.x, event->area.y,
410 event->area.width, event->area.height);
412 g_object_unref (pixmap);
413 g_object_unref (gc);
415 return true;
418 gboolean
419 MoonWindowGtk::button_press (GtkWidget *widget, GdkEventButton *event, gpointer data)
421 MoonWindowGtk *window = (MoonWindowGtk*)data;
423 Deployment::SetCurrent (window->GetDeployment ());
425 if (event->button != 1 && event->button != 3)
426 return false;
428 if (window->surface)
429 window->surface->HandleUIButtonPress (event);
431 // If we don't support right clicks (i.e. inside the browser)
432 // return false here
433 if (event->button == 3 && (moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) == 0)
434 return false;
436 // ignore HandleUIButtonPress's return value, and always
437 // return true here, or it gets bubbled up to firefox.
438 return true;
441 gboolean
442 MoonWindowGtk::button_release (GtkWidget *widget, GdkEventButton *event, gpointer data)
444 MoonWindowGtk *window = (MoonWindowGtk*)data;
446 Deployment::SetCurrent (window->GetDeployment ());
448 if (window->surface)
449 window->surface->HandleUIButtonRelease (event);
450 // ignore HandleUIButtonRelease's return value, and always
451 // return true here, or it gets bubbled up to firefox.
452 return true;
455 gboolean
456 MoonWindowGtk::scroll (GtkWidget *widget, GdkEventScroll *event, gpointer data)
458 MoonWindowGtk *window = (MoonWindowGtk*)data;
460 Deployment::SetCurrent (window->GetDeployment ());
462 if (window->surface)
463 window->surface->HandleUIScroll (event);
464 // ignore HandleUIScroll's return value, and always
465 // return true here, or it gets bubbled up to firefox.
466 return true;
469 gboolean
470 MoonWindowGtk::motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
472 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
474 Deployment::SetCurrent (window->GetDeployment ());
476 if (window->surface)
477 window->surface->HandleUIMotion (event);
478 // ignore HandleUIMotion's return value, and always
479 // return true here, or it gets bubbled up to firefox.
480 return true;
483 gboolean
484 MoonWindowGtk::crossing_notify (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
486 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
488 Deployment::SetCurrent (window->GetDeployment ());
490 if (window->surface) {
491 window->surface->HandleUICrossing (event);
492 return true;
495 return false;
498 gboolean
499 MoonWindowGtk::focus_in (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
501 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
503 Deployment::SetCurrent (window->GetDeployment ());
505 if (window->surface) {
506 window->surface->HandleUIFocusIn (event);
507 return true;
510 return false;
513 gboolean
514 MoonWindowGtk::focus_out (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
516 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
518 Deployment::SetCurrent (window->GetDeployment ());
520 if (window->surface) {
521 window->surface->HandleUIFocusOut (event);
522 return true;
525 return false;
528 gboolean
529 MoonWindowGtk::key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
531 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
533 Deployment::SetCurrent (window->GetDeployment ());
535 if (window->surface) {
536 window->surface->HandleUIKeyPress (event);
537 return true;
540 return false;
543 gboolean
544 MoonWindowGtk::key_release (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
546 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
548 Deployment::SetCurrent (window->GetDeployment ());
550 if (window->surface) {
551 window->surface->HandleUIKeyRelease (event);
552 return true;
555 return false;
558 void
559 MoonWindowGtk::widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer data)
561 MoonWindowGtk *window = (MoonWindowGtk*)data;
563 Deployment::SetCurrent (window->GetDeployment ());
565 //printf ("Surface::size-allocate callback: current = %dx%d; new = %dx%d\n",
566 // s->width, s->height, allocation->width, allocation->height);
568 bool emit_resize = false;
570 if (window->width != allocation->width || window->height != allocation->height) {
571 window->width = allocation->width;
572 window->height = allocation->height;
574 emit_resize = true;
577 if (window->surface)
578 window->surface->HandleUIWindowAllocation (emit_resize);
581 void
582 MoonWindowGtk::widget_destroyed (GtkWidget *widget, gpointer user_data)
584 MoonWindowGtk* window = (MoonWindowGtk*)user_data;
586 window->widget = NULL;
587 if (window->surface)
588 window->surface->HandleUIWindowDestroyed (window);
591 gboolean
592 MoonWindowGtk::realized (GtkWidget *widget, gpointer user_data)
594 MoonWindowGtk* window = (MoonWindowGtk*)user_data;
596 #ifdef USE_XRANDR
597 #if INTEL_DRIVERS_STOP_SUCKING
598 // apparently the i965 drivers blank external screens when
599 // getting the screen info (um, ugh?). needless to say, this
600 // annoyance is worse than not using the monitor's refresh as
601 // the upper bound for our fps.
603 // http://lists.freedesktop.org/archives/xorg/2007-August/027616.html
604 int event_base, error_base;
605 GdkWindow *gdk_root = gtk_widget_get_root_window (widget);
606 Display *dpy = GDK_WINDOW_XDISPLAY(gdk_root);
607 Window root = GDK_WINDOW_XID (gdk_root);
608 if (XRRQueryExtension (dpy, &event_base, &error_base)) {
609 XRRScreenConfiguration *info = XRRGetScreenInfo (dpy,
610 root);
611 short rate = XRRConfigCurrentRate (info);
612 printf ("screen refresh rate = %d\n", rate);
613 if (window->surface)
614 window->surface->GetTimeManager()->SetMaximumRefreshRate (rate);
615 XRRFreeScreenConfigInfo (info);
617 #endif
618 #endif
620 Deployment::SetCurrent (window->GetDeployment ());
622 if (window->surface) {
623 window->surface->HandleUIWindowUnavailable ();
624 window->surface->HandleUIWindowAvailable ();
627 #if SANITY
628 Deployment::SetCurrent (NULL);
629 #endif
631 return true;
634 gboolean
635 MoonWindowGtk::unrealized (GtkWidget *widget, gpointer user_data)
637 MoonWindowGtk* window = (MoonWindowGtk*)user_data;
639 Deployment::SetCurrent (window->GetDeployment ());
641 if (window->surface)
642 window->surface->HandleUIWindowUnavailable ();
644 #if SANITY
645 Deployment::SetCurrent (NULL);
646 #endif
648 return true;