/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2015, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ /* A wrapper class to OS dependent event and window interfaces, GTK+ version */ #include #include #include #include "C4Version.h" #include #include #include #include #include #include #include #include #include #include "C4MouseControl.h" #include #include #include #ifdef GDK_WINDOWING_X11 #include #include #include // Some helper functions for choosing a proper visual namespace { static const std::map base_attrib_map { {GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT}, {GLX_X_RENDERABLE, True}, {GLX_RED_SIZE, 4}, {GLX_GREEN_SIZE, 4}, {GLX_BLUE_SIZE, 4}, {GLX_DEPTH_SIZE, 8} }; // Turns an int->int map into an attribute list suitable for any GLX calls. std::unique_ptr MakeGLXAttribList(const std::map &map) { // We need two ints for every attribute, plus one as a sentinel auto list = std::make_unique(map.size() * 2 + 1); int *cursor = list.get(); for(const auto &attrib : map) { *cursor++ = attrib.first; *cursor++ = attrib.second; } *cursor = None; return list; } // This function picks an acceptable GLXFBConfig. To do this, we first // request a list of framebuffer configs with no less than 4 bits per color; // no less than 8 bits of depth buffer; if multisampling is not -1, // with at least the requested number of samples; and with double buffering. // If that returns no suitable configs, we retry with only a single buffer. GLXFBConfig PickGLXFBConfig(Display* dpy, int multisampling) { std::map attrib_map = base_attrib_map; if (multisampling >= 0) { attrib_map[GLX_SAMPLE_BUFFERS] = multisampling > 0 ? 1 : 0; attrib_map[GLX_SAMPLES] = multisampling; } GLXFBConfig *configs = NULL; int config_count; // Find a double-buffered FB config attrib_map[GLX_DOUBLEBUFFER] = True; std::unique_ptr attribs = MakeGLXAttribList(attrib_map); configs = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs.get(), &config_count); if (config_count == 0) { // If none exists, try to find a single-buffered one if (configs != NULL) XFree(configs); attrib_map[GLX_DOUBLEBUFFER] = False; attribs = MakeGLXAttribList(attrib_map); configs = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs.get(), &config_count); } GLXFBConfig config = NULL; if (config_count > 0) { config = configs[0]; } XFree(configs); return config; } } #elif defined(GDK_WINDOWING_WIN32) #include #include #endif // GDK_WINDOWING_X11 static void OnDestroyStatic(GtkWidget* widget, gpointer data) { C4Window* wnd = static_cast(data); wnd->Clear(); } static gboolean OnDelete(GtkWidget* widget, GdkEvent* event, gpointer data) { C4Window* wnd = static_cast(data); wnd->Close(); return true; } #ifdef GDK_WINDOWING_X11 static constexpr int x11scancodeoffset = 8; #else static constexpr int x11scancodeoffset = 0; #endif static gboolean OnKeyPress(GtkWidget* widget, GdkEventKey* event, gpointer data) { C4Window* wnd = static_cast(data); if (event->hardware_keycode <= x11scancodeoffset) return false; Game.DoKeyboardInput(event->hardware_keycode - x11scancodeoffset, KEYEV_Down, !!(event->state & GDK_MOD1_MASK), !!(event->state & GDK_CONTROL_MASK), !!(event->state & GDK_SHIFT_MASK), false, NULL); wnd->CharIn(event->string); // FIXME: Use GtkIMContext somehow return true; } static gboolean OnKeyRelease(GtkWidget* widget, GdkEventKey* event, gpointer user_data) { if (event->hardware_keycode <= x11scancodeoffset) return false; Game.DoKeyboardInput(event->hardware_keycode - x11scancodeoffset, KEYEV_Up, !!(event->state & GDK_MOD1_MASK), !!(event->state & GDK_CONTROL_MASK), !!(event->state & GDK_SHIFT_MASK), false, NULL); return true; } static void OnDragDataReceivedStatic(GtkWidget* widget, GdkDragContext* context, gint x, gint y, GtkSelectionData* data, guint info, guint time, gpointer user_data) { if (!Console.Editing) { Console.Message(LoadResStr("IDS_CNS_NONETEDIT")); return; } C4ViewportWindow* window = static_cast(user_data); gchar** uris = gtk_selection_data_get_uris(data); if (!uris) return; for (gchar** uri = uris; *uri != NULL; ++ uri) { gchar* file = g_filename_from_uri(*uri, NULL, NULL); if (!file) continue; window->cvp->DropFile(file, x, y); g_free(file); } g_strfreev(uris); } static gboolean OnExposeStatic(GtkWidget* widget, void *, gpointer user_data) { C4Viewport* cvp = static_cast(user_data)->cvp; cvp->Execute(); return true; } static void OnRealizeStatic(GtkWidget* widget, gpointer user_data) { // Initial PlayerLock if (static_cast(user_data)->cvp->GetPlayerLock()) { gtk_widget_hide(static_cast(user_data)->h_scrollbar); gtk_widget_hide(static_cast(user_data)->v_scrollbar); } } static gboolean OnKeyPressStatic(GtkWidget* widget, GdkEventKey* event, gpointer user_data) { if (event->keyval == GDK_KEY_Scroll_Lock) { static_cast(user_data)->cvp->TogglePlayerLock(); return true; } if (event->hardware_keycode <= x11scancodeoffset) return false; Console.EditCursor.KeyDown(event->hardware_keycode - x11scancodeoffset, event->state); return false; } static gboolean OnKeyReleaseStatic(GtkWidget* widget, GdkEventKey* event, gpointer user_data) { if (event->hardware_keycode <= x11scancodeoffset) return false; Console.EditCursor.KeyUp(event->hardware_keycode - x11scancodeoffset, event->state); return false; } static gboolean OnScrollVW(GtkWidget* widget, GdkEventScroll* event, gpointer user_data) { C4ViewportWindow* window = static_cast(user_data); if (::MouseControl.IsViewport(window->cvp) && (Console.EditCursor.GetMode()==C4CNS_ModePlay)) { switch (event->direction) { case GDK_SCROLL_UP: C4GUI::MouseMove(C4MC_Button_Wheel, (int32_t)event->x, (int32_t)event->y, event->state + (short(1) << 16), window->cvp); break; case GDK_SCROLL_DOWN: C4GUI::MouseMove(C4MC_Button_Wheel, (int32_t)event->x, (int32_t)event->y, event->state + (short(-1) << 16), window->cvp); break; default: return false; } } return true; } static gboolean OnButtonPressStatic(GtkWidget* widget, GdkEventButton* event, gpointer user_data) { C4ViewportWindow* window = static_cast(user_data); if (::MouseControl.IsViewport(window->cvp) && (Console.EditCursor.GetMode()==C4CNS_ModePlay)) { switch (event->button) { case 1: if (event->type == GDK_BUTTON_PRESS) C4GUI::MouseMove(C4MC_Button_LeftDown, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp); else if (event->type == GDK_2BUTTON_PRESS) C4GUI::MouseMove(C4MC_Button_LeftDouble, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp); break; case 2: C4GUI::MouseMove(C4MC_Button_MiddleDown, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp); break; case 3: if (event->type == GDK_BUTTON_PRESS) C4GUI::MouseMove(C4MC_Button_RightDown, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp); else if (event->type == GDK_2BUTTON_PRESS) C4GUI::MouseMove(C4MC_Button_RightDouble, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp); break; } } else { switch (event->button) { case 1: Console.EditCursor.LeftButtonDown(event->state); break; case 3: Console.EditCursor.RightButtonDown(event->state); break; } } return true; } static gboolean OnButtonReleaseStatic(GtkWidget* widget, GdkEventButton* event, gpointer user_data) { C4ViewportWindow* window = static_cast(user_data); if (::MouseControl.IsViewport(window->cvp) && (Console.EditCursor.GetMode()==C4CNS_ModePlay)) { switch (event->button) { case 1: C4GUI::MouseMove(C4MC_Button_LeftUp, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp); break; case 2: C4GUI::MouseMove(C4MC_Button_MiddleUp, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp); break; case 3: C4GUI::MouseMove(C4MC_Button_RightUp, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp); break; } } else { switch (event->button) { case 1: Console.EditCursor.LeftButtonUp(event->state); break; case 3: Console.EditCursor.RightButtonUp(event->state); break; } } return true; } static gboolean OnMotionNotifyStatic(GtkWidget* widget, GdkEventMotion* event, gpointer user_data) { C4ViewportWindow* window = static_cast(user_data); if (::MouseControl.IsViewport(window->cvp) && (Console.EditCursor.GetMode()==C4CNS_ModePlay)) { C4GUI::MouseMove(C4MC_Button_None, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp); } else { window->EditCursorMove(event->x, event->y, event->state); } return true; } static gboolean OnConfigureStatic(GtkWidget* widget, GdkEventConfigure* event, gpointer user_data) { C4ViewportWindow* window = static_cast(user_data); C4Viewport* cvp = window->cvp; //cvp->UpdateOutputSize(); cvp->ScrollBarsByViewPosition(); return false; } static gboolean OnConfigureDareaStatic(GtkWidget* widget, GdkEventConfigure* event, gpointer user_data) { C4ViewportWindow* window = static_cast(user_data); C4Viewport* cvp = window->cvp; cvp->UpdateOutputSize(); return false; } static void OnVScrollStatic(GtkAdjustment* adjustment, gpointer user_data) { static_cast(user_data)->cvp->ViewPositionByScrollBars(); } static void OnHScrollStatic(GtkAdjustment* adjustment, gpointer user_data) { static_cast(user_data)->cvp->ViewPositionByScrollBars(); } static GtkTargetEntry drag_drop_entries[] = { { const_cast("text/uri-list"), 0, 0 } }; static gboolean OnConfigureNotify(GtkWidget *widget, GdkEvent *event, gpointer user_data) { Application.OnResolutionChanged(event->configure.width, event->configure.height); return false; } static bool fullscreen_needs_restore = false; static gboolean fullscreen_restore(gpointer data) { if (fullscreen_needs_restore) Application.SetVideoMode(Application.GetConfigWidth(), Application.GetConfigHeight(), Config.Graphics.RefreshRate, Config.Graphics.Monitor, Application.FullScreenMode()); fullscreen_needs_restore = false; return FALSE; } static gboolean OnFocusInFS(GtkWidget *widget, GdkEvent *event, gpointer user_data) { Application.Active = true; if (Application.FullScreenMode()) { fullscreen_needs_restore = true; gdk_threads_add_idle(fullscreen_restore, NULL); } return false; } static gboolean OnFocusOutFS(GtkWidget *widget, GdkEvent *event, gpointer user_data) { Application.Active = false; if (Application.FullScreenMode() && Application.GetConfigWidth() != -1) { Application.RestoreVideoMode(); gtk_window_iconify(GTK_WINDOW(widget)); fullscreen_needs_restore = false; } return false; } static gboolean OnButtonPressFS(GtkWidget* widget, GdkEventButton* event, gpointer user_data) { switch (event->button) { case 1: if (event->type == GDK_BUTTON_PRESS) C4GUI::MouseMove(C4MC_Button_LeftDown, (int32_t)event->x, (int32_t)event->y, event->state, NULL); else if (event->type == GDK_2BUTTON_PRESS) C4GUI::MouseMove(C4MC_Button_LeftDouble, (int32_t)event->x, (int32_t)event->y, event->state, NULL); break; case 2: C4GUI::MouseMove(C4MC_Button_MiddleDown, (int32_t)event->x, (int32_t)event->y, event->state, NULL); break; case 3: if (event->type == GDK_BUTTON_PRESS) C4GUI::MouseMove(C4MC_Button_RightDown, (int32_t)event->x, (int32_t)event->y, event->state, NULL); else if (event->type == GDK_2BUTTON_PRESS) C4GUI::MouseMove(C4MC_Button_RightDouble, (int32_t)event->x, (int32_t)event->y, event->state, NULL); break; default: return false; } return true; } gboolean OnButtonRelease(GtkWidget* widget, GdkEventButton* event, gpointer user_data) { int b; switch (event->button) { case 1: b = C4MC_Button_LeftUp; break; case 2: b = C4MC_Button_MiddleUp; break; case 3: b = C4MC_Button_RightUp; break; default: return false; } C4GUI::MouseMove(b, (int32_t)event->x, (int32_t)event->y, event->state, NULL); return true; } static gboolean OnMotionNotify(GtkWidget* widget, GdkEventMotion* event, gpointer user_data) { C4GUI::MouseMove(C4MC_Button_None, (int32_t)event->x, (int32_t)event->y, event->state, NULL); return true; } static gboolean OnScroll(GtkWidget* widget, GdkEventScroll* event, gpointer user_data) { C4GUI::DialogWindow * window = static_cast(user_data); C4GUI::Dialog *pDlg = ::pGUI->GetDialog(window); int idy; switch (event->direction) { case GDK_SCROLL_UP: idy = 32; break; case GDK_SCROLL_DOWN: idy = -32; break; default: return false; } // FIXME: make the GUI api less insane here if (pDlg) ::pGUI->MouseInput(C4MC_Button_Wheel, event->x, event->y, event->state + (idy << 16), pDlg, NULL); else C4GUI::MouseMove(C4MC_Button_Wheel, event->x, event->y, event->state + (idy << 16), NULL); return true; } static gboolean OnButtonPressGD(GtkWidget* widget, GdkEventButton* event, gpointer user_data) { C4GUI::DialogWindow * window = static_cast(user_data); C4GUI::Dialog *pDlg = ::pGUI->GetDialog(window); switch (event->button) { case 1: if (event->type == GDK_2BUTTON_PRESS) { ::pGUI->MouseInput(C4MC_Button_LeftDouble, event->x, event->y, event->state, pDlg, NULL); } else if (event->type == GDK_BUTTON_PRESS) { ::pGUI->MouseInput(C4MC_Button_LeftDown,event->x, event->y, event->state, pDlg, NULL); } break; case 2: if (event->type == GDK_BUTTON_PRESS) ::pGUI->MouseInput(C4MC_Button_MiddleDown, event->x, event->y, event->state, pDlg, NULL); break; case 3: if (event->type == GDK_2BUTTON_PRESS) { ::pGUI->MouseInput(C4MC_Button_RightDouble, event->x, event->y, event->state, pDlg, NULL); } else if (event->type == GDK_BUTTON_PRESS) { ::pGUI->MouseInput(C4MC_Button_RightDown, event->x, event->y, event->state, pDlg, NULL); } break; } return true; } static gboolean OnButtonReleaseGD(GtkWidget* widget, GdkEventButton* event, gpointer user_data) { C4GUI::DialogWindow * window = static_cast(user_data); C4GUI::Dialog *pDlg = ::pGUI->GetDialog(window); switch (event->button) { case 1: ::pGUI->MouseInput(C4MC_Button_LeftUp, event->x, event->y, event->state, pDlg, NULL); break; case 2: ::pGUI->MouseInput(C4MC_Button_MiddleUp, event->x, event->y, event->state, pDlg, NULL); break; case 3: ::pGUI->MouseInput(C4MC_Button_RightUp, event->x, event->y, event->state, pDlg, NULL); break; } return true; } static gboolean OnMotionNotifyGD(GtkWidget* widget, GdkEventMotion* event, gpointer user_data) { C4GUI::DialogWindow * window = static_cast(user_data); C4GUI::Dialog *pDlg = ::pGUI->GetDialog(window); ::pGUI->MouseInput(C4MC_Button_None, event->x, event->y, event->state, pDlg, NULL); return true; } static gboolean OnConfigureGD(GtkWidget* widget, GdkEventConfigure* event, gpointer user_data) { C4GUI::DialogWindow * window = static_cast(user_data); window->pSurface->UpdateSize(event->width, event->height); return false; } C4Window::C4Window (): Active(false), pSurface(0), renderwnd(0), Info(0), window(NULL) { } C4Window::~C4Window () { Clear(); } #ifdef GDK_WINDOWING_X11 bool C4Window::FindFBConfig(int samples, GLXFBConfig *info) { Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); GLXFBConfig config = PickGLXFBConfig(dpy, samples); if (info) { *info = config; } return config != NULL; return false; } #endif void C4Window::EnumerateMultiSamples(std::vector& samples) const { #ifdef GDK_WINDOWING_X11 Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); std::map attribs = base_attrib_map; attribs[GLX_SAMPLE_BUFFERS_ARB] = 1; int config_count = 0; GLXFBConfig *configs = glXChooseFBConfig(dpy, DefaultScreen(dpy), MakeGLXAttribList(attribs).get(), &config_count); std::set multisamples; for(int i = 0; i < config_count; ++i) { int v_samples; glXGetFBConfigAttrib(dpy, configs[i], GLX_SAMPLES, &v_samples); multisamples.insert(v_samples); } XFree(configs); samples.assign(multisamples.cbegin(), multisamples.cend()); #else if(pGL && pGL->pMainCtx) samples = pGL->pMainCtx->EnumerateMultiSamples(); #endif } bool C4Window::StorePosition(const char *, const char *, bool) { return true; } bool C4Window::RestorePosition(const char *, const char *, bool) { // The Windowmanager is responsible for window placement. return true; } void C4Window::FlashWindow() { //FIXME - how is this reset? gtk_window_set_urgency_hint(window, true); } C4Window* C4Window::Init(WindowKind windowKind, C4AbstractApp * pApp, const char * Title, const C4Rect * size) { Active = true; #ifdef GDK_WINDOWING_X11 if(!FindFBConfig(Config.Graphics.MultiSampling, &Info)) { // Disable multisampling if we don't find a visual which // supports the currently configured setting. if(!FindFBConfig(0, &Info)) return NULL; Config.Graphics.MultiSampling = 0; } #endif assert(!window); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); if (windowKind == W_Viewport) { C4ViewportWindow * vw = static_cast(this); // Cannot just use ScrolledWindow because this would just move // the GdkWindow of the DrawingArea. GtkWidget* table = gtk_grid_new(); render_widget = gtk_drawing_area_new(); gtk_widget_set_hexpand(GTK_WIDGET(render_widget), true); gtk_widget_set_vexpand(GTK_WIDGET(render_widget), true); gtk_grid_attach(GTK_GRID(table), GTK_WIDGET(render_widget), 0, 0, 1, 1); vw->h_scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL); gtk_grid_attach(GTK_GRID(table), vw->h_scrollbar, 0, 1, 1, 1); vw->v_scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL); gtk_grid_attach(GTK_GRID(table), vw->v_scrollbar, 1, 0, 1, 1); GtkAdjustment* adjustment = gtk_range_get_adjustment(GTK_RANGE(vw->h_scrollbar)); g_signal_connect( G_OBJECT(adjustment), "value-changed", G_CALLBACK(OnHScrollStatic), this ); adjustment = gtk_range_get_adjustment(GTK_RANGE(vw->v_scrollbar)); g_signal_connect( G_OBJECT(adjustment), "value-changed", G_CALLBACK(OnVScrollStatic), this ); gtk_container_add(GTK_CONTAINER(window), table); gtk_widget_add_events(GTK_WIDGET(window), GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK | GDK_POINTER_MOTION_MASK); gtk_drag_dest_set(GTK_WIDGET(render_widget), GTK_DEST_DEFAULT_ALL, drag_drop_entries, 1, GDK_ACTION_COPY); g_signal_connect(G_OBJECT(render_widget), "drag-data-received", G_CALLBACK(OnDragDataReceivedStatic), this); g_signal_connect(G_OBJECT(render_widget), "draw", G_CALLBACK(OnExposeStatic), this); g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(OnKeyPressStatic), this); g_signal_connect(G_OBJECT(window), "key-release-event", G_CALLBACK(OnKeyReleaseStatic), this); g_signal_connect(G_OBJECT(window), "scroll-event", G_CALLBACK(OnScrollVW), this); g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(OnButtonPressStatic), this); g_signal_connect(G_OBJECT(window), "button-release-event", G_CALLBACK(OnButtonReleaseStatic), this); g_signal_connect(G_OBJECT(window), "motion-notify-event", G_CALLBACK(OnMotionNotifyStatic), this); g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(OnKeyPress), this); g_signal_connect(G_OBJECT(window), "key-release-event", G_CALLBACK(OnKeyRelease), this); g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(OnConfigureStatic), this); g_signal_connect(G_OBJECT(window), "realize", G_CALLBACK(OnRealizeStatic), this); g_signal_connect_after(G_OBJECT(render_widget), "configure-event", G_CALLBACK(OnConfigureDareaStatic), this); #if !GTK_CHECK_VERSION(3,10,0) // do not draw the default background gtk_widget_set_double_buffered (GTK_WIDGET(render_widget), false); #endif gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(Console.window)); #if !GTK_CHECK_VERSION(3,14,0) gtk_window_set_has_resize_grip(GTK_WINDOW(window), false); #endif } else if (windowKind == W_Fullscreen) { render_widget = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(render_widget)); g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(OnConfigureNotify), this); g_signal_connect(G_OBJECT(window), "focus-in-event", G_CALLBACK(OnFocusInFS), this); g_signal_connect(G_OBJECT(window), "focus-out-event", G_CALLBACK(OnFocusOutFS), this); g_signal_connect(G_OBJECT(window), "unmap-event", G_CALLBACK(OnFocusOutFS), this); g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(OnButtonPressFS), this); g_signal_connect(G_OBJECT(window), "button-release-event", G_CALLBACK(OnButtonRelease), this); g_signal_connect(G_OBJECT(window), "motion-notify-event", G_CALLBACK(OnMotionNotify), this); g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(OnKeyPress), this); g_signal_connect(G_OBJECT(window), "key-release-event", G_CALLBACK(OnKeyRelease), this); g_signal_connect(G_OBJECT(window), "scroll-event", G_CALLBACK(OnScroll), this); gtk_widget_add_events(GTK_WIDGET(window), GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); #if !GTK_CHECK_VERSION(3,10,0) gtk_widget_set_double_buffered (GTK_WIDGET(render_widget), false); #endif GValue val = {0,{{0}}}; g_value_init (&val, G_TYPE_BOOLEAN); g_value_set_boolean (&val, true); g_object_set_property (G_OBJECT (render_widget), "can-focus", &val); g_object_set_property (G_OBJECT (window), "can-focus", &val); g_value_unset (&val); #if !GTK_CHECK_VERSION(3,14,0) gtk_window_set_has_resize_grip(GTK_WINDOW(window), false); #endif } else if (windowKind == W_GuiWindow) { render_widget = window; g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(OnButtonPressGD), this); g_signal_connect(G_OBJECT(window), "button-release-event", G_CALLBACK(OnButtonReleaseGD), this); g_signal_connect(G_OBJECT(window), "motion-notify-event", G_CALLBACK(OnMotionNotifyGD), this); g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(OnConfigureGD), this); g_signal_connect(G_OBJECT(window), "scroll-event", G_CALLBACK(OnScroll), this); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(Console.window)); #if !GTK_CHECK_VERSION(3,14,0) gtk_window_set_has_resize_grip(GTK_WINDOW(window), false); #endif } else if (windowKind == W_Console) { render_widget = window; } assert(window); assert(render_widget); // Override gtk's default to match name/class of the XLib windows gtk_window_set_wmclass(GTK_WINDOW(window), C4ENGINENAME, C4ENGINENAME); gtk_window_set_default_size(GTK_WINDOW(window), size->Wdt, size->Hgt); g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(OnDelete), this); handlerDestroy = g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(OnDestroyStatic), this); gtk_widget_add_events(GTK_WIDGET(window), GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_SCROLL_MASK); // TODO: It would be nice to support GDK_SCROLL_SMOOTH_MASK and // smooth scrolling for scrolling in menus, however that should not // change the scroll wheel behaviour ingame for zooming or // inventory change. Note that when both GDK_SCROLL_MASK and // GDK_SMOOTH_SCROLL_MASK are enabled, both type of scroll events // are reported, so one needs to make sure to not double-process them. // It would be nice to have smooth scrolling also e.g. for zooming // ingame, but it probably requires the notion of smooth scrolling // other parts of the engine as well. #ifdef GDK_WINDOWING_X11 GdkScreen * scr = gtk_widget_get_screen(GTK_WIDGET(render_widget)); Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); XVisualInfo *vis_info = glXGetVisualFromFBConfig(dpy, Info); assert(vis_info); GdkVisual * vis = gdk_x11_screen_lookup_visual(scr, vis_info->visualid); XFree(vis_info); gtk_widget_set_visual(GTK_WIDGET(render_widget),vis); #endif gtk_widget_show_all(GTK_WIDGET(window)); // XVisualInfo vitmpl; int blub; // vitmpl.visual = gdk_x11_visual_get_xvisual(gtk_widget_get_visual(window)); // vitmpl.visualid = XVisualIDFromVisual(vitmpl.visual); // Info = XGetVisualInfo(dpy, VisualIDMask, &vitmpl, &blub); // printf("%p\n", gtk_widget_get_visual(render_widget)); // Info = gdk_x11_visual_get_xvisual(gtk_widget_get_visual(render_widget)); // Default icon has been set right after gtk_init(), // so we don't need to take care about setting the icon here. SetTitle(Title); GdkWindow* window_wnd = gtk_widget_get_window(GTK_WIDGET(window)); // Wait until window is mapped to get the window's XID gtk_widget_show_now(GTK_WIDGET(window)); GdkWindow* render_gdk_wnd; if (GTK_IS_LAYOUT(render_widget)) render_gdk_wnd = gtk_layout_get_bin_window(GTK_LAYOUT(render_widget)); else render_gdk_wnd = gtk_widget_get_window(GTK_WIDGET(render_widget)); #ifdef GDK_WINDOWING_X11 renderwnd = GDK_WINDOW_XID(render_gdk_wnd); #elif defined(GDK_WINDOWING_WIN32) renderwnd = reinterpret_cast(gdk_win32_window_get_handle(render_gdk_wnd)); #endif // Make sure the window is shown and ready to be rendered into, // this avoids an async X error. gdk_flush(); if (windowKind == W_Fullscreen) gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(render_widget)), gdk_cursor_new_for_display(gdk_display_get_default(), GDK_BLANK_CURSOR)); return this; } bool C4Window::ReInit(C4AbstractApp* pApp) { // Check whether multisampling settings was changed. If not then we // don't need to ReInit anything. #ifdef GDK_WINDOWING_X11 int value; Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); glXGetFBConfigAttrib(dpy, Info, GLX_SAMPLES, &value); if(value == Config.Graphics.MultiSampling) return true; // Check whether we have a visual with the requested number of samples GLXFBConfig new_info; if(!FindFBConfig(Config.Graphics.MultiSampling, &new_info)) return false; GdkScreen * scr = gtk_widget_get_screen(GTK_WIDGET(render_widget)); XVisualInfo *vis_info = glXGetVisualFromFBConfig(dpy, new_info); assert(vis_info); GdkVisual * vis = gdk_x11_screen_lookup_visual(scr, vis_info->visualid); XFree(vis_info); // Un- and re-realizing the render_widget does not work, the window // remains hidden afterwards. So we re-create it from scratch. gtk_widget_destroy(GTK_WIDGET(render_widget)); render_widget = gtk_drawing_area_new(); #if !GTK_CHECK_VERSION(3,10,0) gtk_widget_set_double_buffered (GTK_WIDGET(render_widget), false); #endif g_object_set(G_OBJECT(render_widget), "can-focus", TRUE, NULL); gtk_widget_set_visual(GTK_WIDGET(render_widget),vis); Info = new_info; // Wait until window is mapped to get the window's XID gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(render_widget)); gtk_widget_show_now(GTK_WIDGET(render_widget)); if (GTK_IS_LAYOUT(render_widget)) { GdkWindow* bin_wnd = gtk_layout_get_bin_window(GTK_LAYOUT(render_widget)); renderwnd = GDK_WINDOW_XID(bin_wnd); } else { GdkWindow* render_wnd = gtk_widget_get_window(GTK_WIDGET(render_widget)); renderwnd = GDK_WINDOW_XID(render_wnd); } gdk_flush(); gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(render_widget)), gdk_cursor_new_for_display(gdk_display_get_default(), GDK_BLANK_CURSOR)); return true; #endif } void C4Window::Clear() { if (window != NULL) { g_signal_handler_disconnect(window, handlerDestroy); gtk_widget_destroy(GTK_WIDGET(window)); handlerDestroy = 0; } // Avoid that the base class tries to free these renderwnd = 0; window = NULL; Active = false; Info = 0; } void C4Window::SetSize(unsigned int width, unsigned int height) { gtk_window_resize(GTK_WINDOW(window), width, height); } bool C4Window::GetSize(C4Rect * r) { r->x = 0; r->y = 0; gtk_window_get_size(GTK_WINDOW(window), &r->Wdt, &r->Hgt); return true; } void C4Window::SetTitle(char const * Title) { gtk_window_set_title(GTK_WINDOW(window), Title); } void C4Window::RequestUpdate() { // just invoke directly PerformUpdate(); }