1 /* gtkwindowpeer.c -- Native implementation of GtkWindowPeer
2 Copyright (C) 1998, 1999, 2002, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
40 #include "gnu_java_awt_peer_gtk_GtkWindowPeer.h"
41 #include <gdk/gdkprivate.h>
43 #include <X11/Xatom.h>
45 /* FIXME: we're currently seeing the double-activation that occurs
46 with metacity and GTK. See
47 http://bugzilla.gnome.org/show_bug.cgi?id=140977 for details. */
49 static void window_get_frame_extents (GtkWidget
*window
,
51 int *bottom
, int *right
);
53 static void request_frame_extents (GtkWidget
*window
);
55 static Bool
property_notify_predicate (Display
*display
,
59 static void window_delete_cb (GtkWidget
*widget
, GdkEvent
*event
,
61 static void window_destroy_cb (GtkWidget
*widget
, GdkEvent
*event
,
63 static void window_show_cb (GtkWidget
*widget
, jobject peer
);
64 static void window_active_state_change_cb (GtkWidget
*widget
,
67 static void window_focus_state_change_cb (GtkWidget
*widget
,
70 static gboolean
window_focus_in_cb (GtkWidget
* widget
,
73 static gboolean
window_focus_out_cb (GtkWidget
* widget
,
76 static gboolean
window_window_state_cb (GtkWidget
*widget
,
79 static jint
window_get_new_state (GtkWidget
*widget
);
80 static gboolean
window_property_changed_cb (GtkWidget
*widget
,
81 GdkEventProperty
*event
,
83 static void realize_cb (GtkWidget
*widget
, jobject peer
);
85 /* Union used for type punning. */
89 unsigned long **extents
;
98 JNIEXPORT
void JNICALL
99 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_create
100 (JNIEnv
*env
, jobject obj
, jint type
, jboolean decorated
, jobject parent
)
102 GtkWidget
*window_widget
;
107 NSA_SET_GLOBAL_REF (env
, obj
);
109 gdk_threads_enter ();
111 window_widget
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
112 window
= GTK_WINDOW (window_widget
);
114 /* Keep this window in front of its parent, if it has one. */
117 window_parent
= NSA_GET_PTR (env
, parent
);
118 gtk_window_set_transient_for (window
, GTK_WINDOW(window_parent
));
121 gtk_window_set_decorated (window
, decorated
);
123 gtk_window_set_type_hint (window
, type
);
125 gtk_window_group_add_window (global_gtk_window_group
, window
);
127 fixed
= gtk_fixed_new ();
128 gtk_container_add (GTK_CONTAINER (window_widget
), fixed
);
130 gtk_widget_show (fixed
);
132 gdk_threads_leave ();
134 NSA_SET_PTR (env
, obj
, window_widget
);
137 JNIEXPORT
void JNICALL
138 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_gtkWindowSetTitle
139 (JNIEnv
*env
, jobject obj
, jstring title
)
144 ptr
= NSA_GET_PTR (env
, obj
);
146 c_title
= (*env
)->GetStringUTFChars (env
, title
, NULL
);
148 gdk_threads_enter ();
150 gtk_window_set_title (GTK_WINDOW (ptr
), c_title
);
152 gdk_threads_leave ();
154 (*env
)->ReleaseStringUTFChars (env
, title
, c_title
);
157 JNIEXPORT
void JNICALL
158 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_gtkWindowSetResizable
159 (JNIEnv
*env
, jobject obj
, jboolean resizable
)
163 ptr
= NSA_GET_PTR (env
, obj
);
165 gdk_threads_enter ();
167 gtk_window_set_policy (GTK_WINDOW (ptr
), resizable
, resizable
, FALSE
);
169 gdk_threads_leave ();
172 JNIEXPORT
void JNICALL
173 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_gtkWindowSetModal
174 (JNIEnv
*env
, jobject obj
, jboolean modal
)
178 ptr
= NSA_GET_PTR (env
, obj
);
180 gdk_threads_enter ();
182 gtk_window_set_modal (GTK_WINDOW (ptr
), modal
);
184 gdk_threads_leave ();
187 JNIEXPORT
void JNICALL
188 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_nativeSetVisible
189 (JNIEnv
*env
, jobject obj
, jboolean visible
)
193 ptr
= NSA_GET_PTR (env
, obj
);
195 gdk_threads_enter ();
198 gtk_widget_show (GTK_WIDGET (ptr
));
200 gtk_widget_hide (GTK_WIDGET (ptr
));
202 XFlush (GDK_DISPLAY ());
204 gdk_threads_leave ();
207 JNIEXPORT
void JNICALL
208 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_connectSignals
209 (JNIEnv
*env
, jobject obj
)
214 ptr
= NSA_GET_PTR (env
, obj
);
215 gref
= NSA_GET_GLOBAL_REF (env
, obj
);
217 gdk_threads_enter ();
219 g_signal_connect (G_OBJECT (ptr
), "event",
220 G_CALLBACK (pre_event_handler
), *gref
);
222 g_signal_connect (G_OBJECT (ptr
), "delete-event",
223 G_CALLBACK (window_delete_cb
), *gref
);
225 g_signal_connect (G_OBJECT (ptr
), "destroy-event",
226 G_CALLBACK (window_destroy_cb
), *gref
);
228 g_signal_connect (G_OBJECT (ptr
), "show",
229 G_CALLBACK (window_show_cb
), *gref
);
231 g_signal_connect (G_OBJECT (ptr
), "notify::is-active",
232 G_CALLBACK (window_active_state_change_cb
), *gref
);
234 g_signal_connect (G_OBJECT (ptr
), "notify::has-toplevel-focus",
235 G_CALLBACK (window_focus_state_change_cb
), *gref
);
237 g_signal_connect (G_OBJECT (ptr
), "focus-in-event",
238 G_CALLBACK (window_focus_in_cb
), *gref
);
240 g_signal_connect (G_OBJECT (ptr
), "focus-out-event",
241 G_CALLBACK (window_focus_out_cb
), *gref
);
243 g_signal_connect (G_OBJECT (ptr
), "window-state-event",
244 G_CALLBACK (window_window_state_cb
), *gref
);
246 g_signal_connect (G_OBJECT (ptr
), "property-notify-event",
247 G_CALLBACK (window_property_changed_cb
), *gref
);
249 g_signal_connect_after (G_OBJECT (ptr
), "realize",
250 G_CALLBACK (realize_cb
), *gref
);
252 g_signal_connect_after (G_OBJECT (ptr
), "realize",
253 G_CALLBACK (connect_awt_hook_cb
), *gref
);
255 gdk_threads_leave ();
258 JNIEXPORT
void JNICALL
259 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_toBack (JNIEnv
*env
,
263 ptr
= NSA_GET_PTR (env
, obj
);
265 gdk_threads_enter ();
267 gdk_window_lower (GTK_WIDGET (ptr
)->window
);
270 gdk_threads_leave ();
273 JNIEXPORT
void JNICALL
274 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_toFront (JNIEnv
*env
,
278 ptr
= NSA_GET_PTR (env
, obj
);
280 gdk_threads_enter ();
282 gdk_window_raise (GTK_WIDGET (ptr
)->window
);
285 gdk_threads_leave ();
288 JNIEXPORT
void JNICALL
289 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_setBoundsCallback
290 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)),
291 jobject window
, jint x
, jint y
, jint width
, jint height
)
293 /* Circumvent package-private access to call Window's
294 setBoundsCallback method. */
295 (*gdk_env())->CallVoidMethod (gdk_env(), window
, setBoundsCallbackID
,
296 x
, y
, width
, height
);
299 JNIEXPORT
void JNICALL
300 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_setSize
301 (JNIEnv
*env
, jobject obj
, jint width
, jint height
)
303 void *ptr
= NSA_GET_PTR (env
, obj
);
305 /* Avoid GTK runtime assertion failures. */
306 width
= (width
< 1) ? 1 : width
;
307 height
= (height
< 1) ? 1 : height
;
309 gdk_threads_enter ();
311 gtk_widget_set_size_request (GTK_WIDGET(ptr
), width
, height
);
313 gdk_threads_leave ();
316 JNIEXPORT
void JNICALL
317 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_nativeSetBounds
318 (JNIEnv
*env
, jobject obj
, jint x
, jint y
, jint width
, jint height
)
320 void *ptr
= NSA_GET_PTR (env
, obj
);
322 /* Avoid GTK runtime assertion failures. */
323 width
= (width
< 1) ? 1 : width
;
324 height
= (height
< 1) ? 1 : height
;
326 gdk_threads_enter ();
328 gtk_window_move (GTK_WINDOW(ptr
), x
, y
);
329 /* The call to gdk_window_move is needed in addition to the call to
330 gtk_window_move. If gdk_window_move isn't called, then the
331 following set of operations doesn't give the expected results:
334 2. manually move it to another position on the screen
336 4. reposition the window with Component.setLocation
339 Instead of being at the position set by setLocation, the window
340 is reshown at the position to which it was moved manually. */
341 if (GTK_WIDGET (ptr
)->window
!= NULL
)
342 gdk_window_move (GTK_WIDGET (ptr
)->window
, x
, y
);
344 /* Need to change the widget's request size. */
345 gtk_widget_set_size_request (GTK_WIDGET(ptr
), width
, height
);
346 /* Also need to call gtk_window_resize. If the resize is requested
347 by the program and the window's "resizable" property is true then
348 the size request will not be honoured. */
349 gtk_window_resize (GTK_WINDOW (ptr
), width
, height
);
350 gdk_threads_leave ();
354 window_get_frame_extents (GtkWidget
*window
,
355 int *top
, int *left
, int *bottom
, int *right
)
357 unsigned long *extents
= NULL
;
358 union extents_union gu_ex
;
360 /* Guess frame extents in case _NET_FRAME_EXTENTS is not
367 /* Request that the window manager set window's
368 _NET_FRAME_EXTENTS property. */
369 request_frame_extents (window
);
371 /* Attempt to retrieve window's frame extents. */
372 gu_ex
.extents
= &extents
;
373 if (gdk_property_get (window
->window
,
374 gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE
),
375 gdk_atom_intern ("CARDINAL", FALSE
),
377 sizeof (unsigned long) * 4,
385 *right
= extents
[1];
387 *bottom
= extents
[3];
391 static Atom extents_atom
= 0;
393 /* Requests that the window manager set window's
394 _NET_FRAME_EXTENTS property. */
396 request_frame_extents (GtkWidget
*window
)
398 const char *request_str
= "_NET_REQUEST_FRAME_EXTENTS";
399 GdkAtom request_extents
= gdk_atom_intern (request_str
, FALSE
);
401 /* Check if the current window manager supports
402 _NET_REQUEST_FRAME_EXTENTS. */
403 if (gdk_net_wm_supports (request_extents
))
405 GdkDisplay
*display
= gtk_widget_get_display (window
);
406 Display
*xdisplay
= GDK_DISPLAY_XDISPLAY (display
);
408 GdkWindow
*root_window
= gdk_get_default_root_window ();
409 Window xroot_window
= GDK_WINDOW_XID (root_window
);
411 Atom extents_request_atom
=
412 gdk_x11_get_xatom_by_name_for_display (display
, request_str
);
415 XEvent notify_xevent
;
417 unsigned long window_id
= GDK_WINDOW_XID (GDK_DRAWABLE(window
->window
));
421 const char *extents_str
= "_NET_FRAME_EXTENTS";
423 gdk_x11_get_xatom_by_name_for_display (display
, extents_str
);
426 xevent
.xclient
.type
= ClientMessage
;
427 xevent
.xclient
.message_type
= extents_request_atom
;
428 xevent
.xclient
.display
= xdisplay
;
429 xevent
.xclient
.window
= window_id
;
430 xevent
.xclient
.format
= 32;
431 xevent
.xclient
.data
.l
[0] = 0;
432 xevent
.xclient
.data
.l
[1] = 0;
433 xevent
.xclient
.data
.l
[2] = 0;
434 xevent
.xclient
.data
.l
[3] = 0;
435 xevent
.xclient
.data
.l
[4] = 0;
437 XSendEvent (xdisplay
, xroot_window
, False
,
438 (SubstructureRedirectMask
| SubstructureNotifyMask
),
441 XIfEvent(xdisplay
, ¬ify_xevent
,
442 property_notify_predicate
, (XPointer
) &window_id
);
447 property_notify_predicate (Display
*xdisplay
__attribute__((unused
)),
451 unsigned long *window
= (unsigned long *) window_id
;
453 if (event
->xany
.type
== PropertyNotify
454 && event
->xany
.window
== *window
455 && event
->xproperty
.atom
== extents_atom
)
462 window_delete_cb (GtkWidget
*widget
__attribute__((unused
)),
463 GdkEvent
*event
__attribute__((unused
)),
466 gdk_threads_leave ();
467 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
469 (jint
) AWT_WINDOW_CLOSING
,
470 (jobject
) NULL
, (jint
) 0);
471 gdk_threads_enter ();
475 window_destroy_cb (GtkWidget
*widget
__attribute__((unused
)),
476 GdkEvent
*event
__attribute__((unused
)),
479 gdk_threads_leave ();
480 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
482 (jint
) AWT_WINDOW_CLOSED
,
483 (jobject
) NULL
, (jint
) 0);
484 gdk_threads_enter ();
488 window_show_cb (GtkWidget
*widget
__attribute__((unused
)),
491 gdk_threads_leave ();
492 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
494 (jint
) AWT_WINDOW_OPENED
,
495 (jobject
) NULL
, (jint
) 0);
496 gdk_threads_enter ();
500 window_active_state_change_cb (GtkWidget
*widget
__attribute__((unused
)),
501 GParamSpec
*pspec
__attribute__((unused
)),
502 jobject peer
__attribute__((unused
)))
504 /* FIXME: not sure if this is needed or not. */
505 /* Remove the unused attributes if you fix the below. */
507 gdk_threads_leave ();
508 if (GTK_WINDOW (widget
)->is_active
)
509 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
511 (jint
) AWT_WINDOW_GAINED_FOCUS
,
512 (jobject
) NULL
, (jint
) 0);
514 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
516 (jint
) AWT_WINDOW_DEACTIVATED
,
517 (jobject
) NULL
, (jint
) 0);
518 gdk_threads_enter ();
523 window_focus_state_change_cb (GtkWidget
*widget
,
524 GParamSpec
*pspec
__attribute__((unused
)),
527 gdk_threads_leave ();
528 if (GTK_WINDOW (widget
)->has_toplevel_focus
)
529 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
531 (jint
) AWT_WINDOW_ACTIVATED
,
532 (jobject
) NULL
, (jint
) 0);
534 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
536 (jint
) AWT_WINDOW_DEACTIVATED
,
537 (jobject
) NULL
, (jint
) 0);
538 gdk_threads_enter ();
542 window_focus_in_cb (GtkWidget
* widget
__attribute__((unused
)),
543 GdkEventFocus
*event
__attribute__((unused
)),
546 gdk_threads_leave ();
547 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
549 (jint
) AWT_WINDOW_GAINED_FOCUS
,
550 (jobject
) NULL
, (jint
) 0);
551 /* FIXME: somewhere after this is handled, the child window is
552 getting an expose event. */
553 gdk_threads_enter ();
558 window_focus_out_cb (GtkWidget
* widget
__attribute__((unused
)),
559 GdkEventFocus
*event
__attribute__((unused
)),
562 gdk_threads_leave ();
563 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
565 (jint
) AWT_WINDOW_LOST_FOCUS
,
566 (jobject
) NULL
, (jint
) 0);
567 /* FIXME: somewhere after this is handled, the child window is
568 getting an expose event. */
569 gdk_threads_enter ();
574 window_window_state_cb (GtkWidget
*widget
,
580 /* Handle WINDOW_ICONIFIED and WINDOW_DEICONIFIED events. */
581 if (event
->window_state
.changed_mask
& GDK_WINDOW_STATE_ICONIFIED
)
583 /* We've either been iconified or deiconified. */
584 if (event
->window_state
.new_window_state
& GDK_WINDOW_STATE_ICONIFIED
)
586 /* We've been iconified. */
587 gdk_threads_leave ();
588 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
590 (jint
) AWT_WINDOW_ICONIFIED
,
591 (jobject
) NULL
, (jint
) 0);
592 gdk_threads_enter ();
596 /* We've been deiconified. */
597 gdk_threads_leave ();
598 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
600 (jint
) AWT_WINDOW_DEICONIFIED
,
601 (jobject
) NULL
, (jint
) 0);
602 gdk_threads_enter ();
606 /* Post a WINDOW_STATE_CHANGED event, passing the new frame state to
608 new_state
= AWT_FRAME_STATE_NORMAL
;
610 if (event
->window_state
.new_window_state
& GDK_WINDOW_STATE_ICONIFIED
)
611 new_state
|= AWT_FRAME_STATE_ICONIFIED
;
613 new_state
|= window_get_new_state (widget
);
615 gdk_threads_leave ();
616 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
618 (jint
) AWT_WINDOW_STATE_CHANGED
,
619 (jobject
) NULL
, new_state
);
620 gdk_threads_enter ();
625 window_get_new_state (GtkWidget
*widget
)
627 GdkDisplay
*display
= gtk_widget_get_display(widget
);
628 jint new_state
= AWT_FRAME_STATE_NORMAL
;
633 Atom
*atom_list
= NULL
;
634 union atom_list_union alu
;
637 alu
.atom_list
= &atom_list
;
638 XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display
),
639 GDK_WINDOW_XID (widget
->window
),
640 gdk_x11_get_xatom_by_name_for_display (display
, "_NET_WM_STATE"),
641 0, G_MAXLONG
, False
, XA_ATOM
, &type
, &format
, &atom_count
,
642 &bytes_after
, alu
.gu_extents
);
646 Atom maxvert
= gdk_x11_get_xatom_by_name_for_display (display
, "_NET_WM_STATE_MAXIMIZED_VERT");
647 Atom maxhorz
= gdk_x11_get_xatom_by_name_for_display (display
, "_NET_WM_STATE_MAXIMIZED_HORZ");
650 while (i
< atom_count
)
652 if (atom_list
[i
] == maxhorz
)
653 new_state
|= AWT_FRAME_STATE_MAXIMIZED_HORIZ
;
654 else if (atom_list
[i
] == maxvert
)
655 new_state
|= AWT_FRAME_STATE_MAXIMIZED_VERT
;
666 window_property_changed_cb (GtkWidget
*widget
__attribute__((unused
)),
667 GdkEventProperty
*event
,
670 unsigned long *extents
;
671 union extents_union gu_ex
;
673 gu_ex
.extents
= &extents
;
674 if (gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE
) == event
->atom
675 && gdk_property_get (event
->window
,
676 gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE
),
677 gdk_atom_intern ("CARDINAL", FALSE
),
679 sizeof (unsigned long) * 4,
686 gdk_threads_leave ();
687 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
688 postInsetsChangedEventID
,
689 (jint
) extents
[2], /* top */
690 (jint
) extents
[0], /* left */
691 (jint
) extents
[3], /* bottom */
692 (jint
) extents
[1]); /* right */
693 gdk_threads_enter ();
701 realize_cb (GtkWidget
*widget
, jobject peer
)
710 width
= (*gdk_env())->CallIntMethod (gdk_env(), peer
, windowGetWidthID
);
711 height
= (*gdk_env())->CallIntMethod (gdk_env(), peer
, windowGetHeightID
);
713 window_get_frame_extents (widget
, &top
, &left
, &bottom
, &right
);
715 (*gdk_env())->CallVoidMethod (gdk_env(), peer
,
716 postInsetsChangedEventID
,
717 top
, left
, bottom
, right
);
719 gtk_window_set_default_size (GTK_WINDOW (widget
),
720 MAX (1, width
- left
- right
),
721 MAX (1, height
- top
- bottom
));
723 /* set the size like we do in nativeSetBounds */
724 gtk_widget_set_size_request (widget
,
725 MAX (1, width
- left
- right
),
726 MAX (1, height
- top
- bottom
));
728 gtk_window_resize (GTK_WINDOW (widget
),
729 MAX (1, width
- left
- right
),
730 MAX (1, height
- top
- bottom
));