1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * window-gtk.cpp: MoonWindow implementation using gtk widgets.
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
)
21 this->fullscreen
= fullscreen
;
22 this->deployment
= Deployment::GetCurrent ();
25 InitializeFullScreen(parent
);
30 MoonWindowGtk::~MoonWindowGtk ()
32 /* gtk_widget_destroy can cause reentry (into another plugin if this destruction causes layout changes) */
33 DeploymentStack deployment_push_pop
;
36 gtk_widget_destroy (widget
);
40 MoonWindowGtk::GetGdkWindow ()
42 GdkWindow
*parent_window
= gtk_widget_get_parent_window (widget
);
43 if (parent_window
== NULL
)
44 parent_window
= widget
->window
;
46 g_object_ref (parent_window
);
51 MoonWindowGtk::InitializeFullScreen (MoonWindow
*parent
)
53 widget
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
55 // only fullscreen on the monitor the plugin is on
56 GdkWindow
*gdk
= parent
->GetGdkWindow ();
57 int monitor
= gdk_screen_get_monitor_at_window (gdk_screen_get_default (), gdk
);
59 gdk_screen_get_monitor_geometry (gdk_screen_get_default (), monitor
, &bounds
);
61 height
= bounds
.height
;
62 gtk_window_move (GTK_WINDOW (widget
), bounds
.x
, bounds
.y
);
64 gtk_window_fullscreen (GTK_WINDOW (widget
));
74 MoonWindowGtk::InitializeNormal ()
76 if (width
== -1 || height
== -1) {
77 g_warning ("you must specify width and height when creating a non-fullscreen gtk window");
82 widget
= gtk_event_box_new ();
84 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget
), false);
92 MoonWindowGtk::InitializeCommon ()
94 // don't let gtk clear the window we'll do all the drawing.
95 //gtk_widget_set_app_paintable (widget, true);
96 gtk_widget_set_double_buffered (widget
, false);
97 gtk_widget_set_size_request (widget
, width
, height
);
99 g_signal_connect (widget
, "size-allocate", G_CALLBACK (widget_size_allocate
), this);
100 g_signal_connect (widget
, "destroy", G_CALLBACK (widget_destroyed
), this);
102 gtk_widget_add_events (widget
,
103 GDK_POINTER_MOTION_MASK
|
104 //GDK_POINTER_MOTION_HINT_MASK |
106 GDK_KEY_RELEASE_MASK
|
107 GDK_BUTTON_PRESS_MASK
|
108 GDK_BUTTON_RELEASE_MASK
|
109 ((moonlight_flags
& RUNTIME_INIT_DESKTOP_EXTENSIONS
) != 0 ? GDK_SCROLL_MASK
: 0) |
110 GDK_FOCUS_CHANGE_MASK
);
112 GTK_WIDGET_SET_FLAGS (widget
, GTK_CAN_FOCUS
);
116 MoonWindowGtk::Resize (int width
, int height
)
118 gtk_widget_set_size_request (widget
, width
, height
);
119 gtk_widget_queue_resize (widget
);
123 static const char *dot
[] = {
150 static const char *eraser
[] = {
216 " b~,,,,,,,,,;$c ",
217 " de-,,,,,,,,,fg ",
224 MoonWindowGtk::SetBackgroundColor (Color
*color
)
227 gdk_color
.red
= color
->r
* 0xffff;
228 gdk_color
.green
= color
->g
* 0xffff;
229 gdk_color
.blue
= color
->b
* 0xffff;
231 gtk_widget_modify_bg (widget
, GTK_STATE_NORMAL
, &gdk_color
);
233 MoonWindow::SetBackgroundColor (color
);
237 MoonWindowGtk::SetCursor (MouseCursor cursor
)
239 if (widget
->window
) {
243 case MouseCursorDefault
:
246 case MouseCursorArrow
:
247 c
= gdk_cursor_new (GDK_LEFT_PTR
);
249 case MouseCursorHand
:
250 c
= gdk_cursor_new (GDK_HAND2
);
252 case MouseCursorWait
:
253 c
= gdk_cursor_new (GDK_WATCH
);
255 case MouseCursorIBeam
:
256 c
= gdk_cursor_new (GDK_XTERM
);
258 case MouseCursorStylus
:
259 c
= gdk_cursor_new_from_pixbuf (gdk_display_get_default (), gdk_pixbuf_new_from_xpm_data ((const char**) dot
), 0, 0);
261 case MouseCursorEraser
:
262 c
= gdk_cursor_new_from_pixbuf (gdk_display_get_default (), gdk_pixbuf_new_from_xpm_data ((const char**) eraser
), 8, 8);
264 case MouseCursorSizeNS
:
265 c
= gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW
);
267 case MouseCursorSizeWE
:
268 c
= gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW
);
270 case MouseCursorNone
:
271 // Silverlight display no cursor if the enumeration value is invalid (e.g. -1)
273 //from gdk-cursor doc :"To make the cursor invisible, use gdk_cursor_new_from_pixmap() to create a cursor with no pixels in it."
274 GdkPixmap
*empty
= gdk_bitmap_create_from_data (NULL
, "0x00", 1, 1);
275 GdkColor empty_color
= {0, 0, 0, 0};
276 c
= gdk_cursor_new_from_pixmap (empty
, empty
, &empty_color
, &empty_color
, 0, 0);
277 g_object_unref (empty
);
282 gdk_window_set_cursor (widget
->window
, c
);
285 gdk_cursor_unref (c
);
290 MoonWindowGtk::Invalidate (Rect r
)
292 gtk_widget_queue_draw_area (widget
,
293 (int) (widget
->allocation
.x
+ r
.x
),
294 (int) (widget
->allocation
.y
+ r
.y
),
295 (int) r
.width
, (int)r
.height
);
299 MoonWindowGtk::ProcessUpdates ()
302 gdk_window_process_updates (widget
->window
, false);
306 MoonWindowGtk::HandleEvent (XEvent
*event
)
308 // nothing to do here, since we don't pump events into the gtk
309 // window, gtk calls our signal handlers directly.
314 MoonWindowGtk::Show ()
316 gtk_widget_show (widget
);
318 // The window has to be realized for this call to work
319 gtk_widget_set_extension_events (widget
, GDK_EXTENSION_EVENTS_CURSOR
);
320 /* we need to explicitly enable the devices */
321 for (GList
*l
= gdk_devices_list(); l
; l
= l
->next
) {
322 #if THIS_NOLONGER_BREAKS_LARRYS_MOUSE
323 GdkDevice
*device
= GDK_DEVICE(l
->data
);
324 //if (!device->has_cursor)
325 gdk_device_set_mode (device
, GDK_MODE_SCREEN
);
329 GTK_WIDGET_SET_FLAGS (widget
, GTK_CAN_FOCUS
);
333 MoonWindowGtk::Hide ()
335 gtk_widget_hide (widget
);
339 MoonWindowGtk::EnableEvents (bool first
)
341 g_signal_connect (widget
, "expose-event", G_CALLBACK (expose_event
), this);
342 g_signal_connect (widget
, "motion-notify-event", G_CALLBACK (motion_notify
), this);
343 g_signal_connect (widget
, "enter-notify-event", G_CALLBACK (crossing_notify
), this);
344 g_signal_connect (widget
, "leave-notify-event", G_CALLBACK (crossing_notify
), this);
345 g_signal_connect (widget
, "key-press-event", G_CALLBACK (key_press
), this);
346 g_signal_connect (widget
, "key-release-event", G_CALLBACK (key_release
), this);
347 g_signal_connect (widget
, "button-press-event", G_CALLBACK (button_press
), this);
348 g_signal_connect (widget
, "button-release-event", G_CALLBACK (button_release
), this);
349 g_signal_connect (widget
, "scroll-event", G_CALLBACK (scroll
), this);
350 g_signal_connect (widget
, "focus-in-event", G_CALLBACK (focus_in
), this);
351 g_signal_connect (widget
, "focus-out-event", G_CALLBACK (focus_out
), this);
354 g_signal_connect (widget
, "realize", G_CALLBACK (realized
), this);
355 g_signal_connect (widget
, "unrealize", G_CALLBACK (unrealized
), this);
357 if (GTK_WIDGET_REALIZED (widget
))
358 realized (widget
, this);
363 MoonWindowGtk::DisableEvents ()
365 g_signal_handlers_disconnect_matched (widget
, G_SIGNAL_MATCH_DATA
,
366 0, 0, NULL
, NULL
, this);
370 MoonWindowGtk::GrabFocus ()
372 gtk_widget_grab_focus (widget
);
376 MoonWindowGtk::HasFocus ()
378 return GTK_WIDGET_HAS_FOCUS (widget
);
382 MoonWindowGtk::expose_event (GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
)
384 MoonWindowGtk
*window
= (MoonWindowGtk
*)data
;
386 Deployment::SetCurrent (window
->GetDeployment ());
388 if (!window
->surface
)
391 // we draw to a backbuffer pixmap, then transfer the contents
392 // to the widget's window.
393 GdkPixmap
*pixmap
= gdk_pixmap_new (widget
->window
,
394 MAX (event
->area
.width
, 1), MAX (event
->area
.height
, 1), -1);
396 window
->surface
->PaintToDrawable (pixmap
,
397 gdk_drawable_get_visual (widget
->window
),
399 widget
->allocation
.x
,
400 widget
->allocation
.y
,
401 window
->GetTransparent (),
404 GdkGC
*gc
= gdk_gc_new (pixmap
);
406 gdk_gc_set_clip_region (gc
, event
->region
);
408 gdk_draw_drawable (widget
->window
, gc
, pixmap
,
410 event
->area
.x
, event
->area
.y
,
411 event
->area
.width
, event
->area
.height
);
413 g_object_unref (pixmap
);
420 MoonWindowGtk::button_press (GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
422 MoonWindowGtk
*window
= (MoonWindowGtk
*)data
;
424 Deployment::SetCurrent (window
->GetDeployment ());
426 if (event
->button
!= 1 && event
->button
!= 3)
430 window
->surface
->HandleUIButtonPress (event
);
432 // If we don't support right clicks (i.e. inside the browser)
434 if (event
->button
== 3 && (moonlight_flags
& RUNTIME_INIT_DESKTOP_EXTENSIONS
) == 0)
437 // ignore HandleUIButtonPress's return value, and always
438 // return true here, or it gets bubbled up to firefox.
443 MoonWindowGtk::button_release (GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
445 MoonWindowGtk
*window
= (MoonWindowGtk
*)data
;
447 Deployment::SetCurrent (window
->GetDeployment ());
450 window
->surface
->HandleUIButtonRelease (event
);
451 // ignore HandleUIButtonRelease's return value, and always
452 // return true here, or it gets bubbled up to firefox.
457 MoonWindowGtk::scroll (GtkWidget
*widget
, GdkEventScroll
*event
, gpointer data
)
459 MoonWindowGtk
*window
= (MoonWindowGtk
*)data
;
461 Deployment::SetCurrent (window
->GetDeployment ());
464 window
->surface
->HandleUIScroll (event
);
465 // ignore HandleUIScroll's return value, and always
466 // return true here, or it gets bubbled up to firefox.
471 MoonWindowGtk::motion_notify (GtkWidget
*widget
, GdkEventMotion
*event
, gpointer user_data
)
473 MoonWindowGtk
*window
= (MoonWindowGtk
*)user_data
;
475 Deployment::SetCurrent (window
->GetDeployment ());
478 window
->surface
->HandleUIMotion (event
);
479 // ignore HandleUIMotion's return value, and always
480 // return true here, or it gets bubbled up to firefox.
485 MoonWindowGtk::crossing_notify (GtkWidget
*widget
, GdkEventCrossing
*event
, gpointer user_data
)
487 MoonWindowGtk
*window
= (MoonWindowGtk
*)user_data
;
489 Deployment::SetCurrent (window
->GetDeployment ());
491 if (window
->surface
) {
492 window
->surface
->HandleUICrossing (event
);
500 MoonWindowGtk::focus_in (GtkWidget
*widget
, GdkEventFocus
*event
, gpointer user_data
)
502 MoonWindowGtk
*window
= (MoonWindowGtk
*)user_data
;
504 Deployment::SetCurrent (window
->GetDeployment ());
506 if (window
->surface
) {
507 window
->surface
->HandleUIFocusIn (event
);
515 MoonWindowGtk::focus_out (GtkWidget
*widget
, GdkEventFocus
*event
, gpointer user_data
)
517 MoonWindowGtk
*window
= (MoonWindowGtk
*)user_data
;
519 Deployment::SetCurrent (window
->GetDeployment ());
521 if (window
->surface
) {
522 window
->surface
->HandleUIFocusOut (event
);
530 MoonWindowGtk::key_press (GtkWidget
*widget
, GdkEventKey
*event
, gpointer user_data
)
532 MoonWindowGtk
*window
= (MoonWindowGtk
*)user_data
;
534 Deployment::SetCurrent (window
->GetDeployment ());
536 if (window
->surface
) {
537 window
->surface
->HandleUIKeyPress (event
);
545 MoonWindowGtk::key_release (GtkWidget
*widget
, GdkEventKey
*event
, gpointer user_data
)
547 MoonWindowGtk
*window
= (MoonWindowGtk
*)user_data
;
549 Deployment::SetCurrent (window
->GetDeployment ());
551 if (window
->surface
) {
552 window
->surface
->HandleUIKeyRelease (event
);
560 MoonWindowGtk::widget_size_allocate (GtkWidget
*widget
, GtkAllocation
*allocation
, gpointer data
)
562 MoonWindowGtk
*window
= (MoonWindowGtk
*)data
;
564 Deployment::SetCurrent (window
->GetDeployment ());
566 //printf ("Surface::size-allocate callback: current = %dx%d; new = %dx%d\n",
567 // s->width, s->height, allocation->width, allocation->height);
569 bool emit_resize
= false;
571 if (window
->width
!= allocation
->width
|| window
->height
!= allocation
->height
) {
572 window
->width
= allocation
->width
;
573 window
->height
= allocation
->height
;
579 window
->surface
->HandleUIWindowAllocation (emit_resize
);
583 MoonWindowGtk::widget_destroyed (GtkWidget
*widget
, gpointer user_data
)
585 MoonWindowGtk
* window
= (MoonWindowGtk
*)user_data
;
587 window
->widget
= NULL
;
589 window
->surface
->HandleUIWindowDestroyed (window
);
593 MoonWindowGtk::realized (GtkWidget
*widget
, gpointer user_data
)
595 MoonWindowGtk
* window
= (MoonWindowGtk
*)user_data
;
598 #if INTEL_DRIVERS_STOP_SUCKING
599 // apparently the i965 drivers blank external screens when
600 // getting the screen info (um, ugh?). needless to say, this
601 // annoyance is worse than not using the monitor's refresh as
602 // the upper bound for our fps.
604 // http://lists.freedesktop.org/archives/xorg/2007-August/027616.html
605 int event_base
, error_base
;
606 GdkWindow
*gdk_root
= gtk_widget_get_root_window (widget
);
607 Display
*dpy
= GDK_WINDOW_XDISPLAY(gdk_root
);
608 Window root
= GDK_WINDOW_XID (gdk_root
);
609 if (XRRQueryExtension (dpy
, &event_base
, &error_base
)) {
610 XRRScreenConfiguration
*info
= XRRGetScreenInfo (dpy
,
612 short rate
= XRRConfigCurrentRate (info
);
613 printf ("screen refresh rate = %d\n", rate
);
615 window
->surface
->GetTimeManager()->SetMaximumRefreshRate (rate
);
616 XRRFreeScreenConfigInfo (info
);
621 Deployment::SetCurrent (window
->GetDeployment ());
623 if (window
->surface
) {
624 window
->surface
->HandleUIWindowUnavailable ();
625 window
->surface
->HandleUIWindowAvailable ();
629 Deployment::SetCurrent (NULL
);
636 MoonWindowGtk::unrealized (GtkWidget
*widget
, gpointer user_data
)
638 MoonWindowGtk
* window
= (MoonWindowGtk
*)user_data
;
640 Deployment::SetCurrent (window
->GetDeployment ());
643 window
->surface
->HandleUIWindowUnavailable ();
646 Deployment::SetCurrent (NULL
);