forked from Mirrors/openclonk
277 lines
7.4 KiB
C++
277 lines
7.4 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2006-2008, 2010 Armin Burgmeier
|
|
* Copyright (c) 2007, 2011 Günther Brammer
|
|
* Copyright (c) 2010 Martin Plicht
|
|
* Copyright (c) 2006-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
*
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
* to OpenClonk.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
* See isc_license.txt for full license and disclaimer.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
* See clonk_trademark_license.txt for full license.
|
|
*/
|
|
|
|
/* A wrapper class to OS dependent event and window interfaces, GTK+ version */
|
|
|
|
#include <C4Include.h>
|
|
#include <C4WindowGTK.h>
|
|
|
|
#include <C4App.h>
|
|
#include "C4Version.h"
|
|
#include "C4Config.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <gdk/gdk.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
/* C4GtkWindow */
|
|
|
|
C4GtkWindow::C4GtkWindow():
|
|
C4Window(), window(NULL)
|
|
{
|
|
}
|
|
|
|
C4GtkWindow::~C4GtkWindow()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
C4Window* C4GtkWindow::Init(WindowKind windowKind, C4AbstractApp * pApp, const char * Title, C4Window * pParent, bool HideCursor)
|
|
{
|
|
Active = true;
|
|
dpy = pApp->dpy;
|
|
|
|
if(!FindInfo(Config.Graphics.MultiSampling, &Info))
|
|
{
|
|
// Disable multisampling if we don't find a visual which
|
|
// supports the currently configured setting.
|
|
if(!FindInfo(0, &Info)) return NULL;
|
|
Config.Graphics.MultiSampling = 0;
|
|
}
|
|
|
|
assert(!window);
|
|
|
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
|
|
// Override gtk's default to match name/class of the XLib windows
|
|
gtk_window_set_wmclass(GTK_WINDOW(window), C4ENGINENAME, C4ENGINENAME);
|
|
|
|
handlerDestroy = g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(OnDestroyStatic), this);
|
|
g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(OnUpdateKeyMask), pApp);
|
|
g_signal_connect(G_OBJECT(window), "key-release-event", G_CALLBACK(OnUpdateKeyMask), pApp);
|
|
|
|
GtkWidget* render_widget = InitGUI();
|
|
|
|
GdkScreen * scr = gtk_widget_get_screen(render_widget);
|
|
GdkVisual * vis = gdk_x11_screen_lookup_visual(scr, ((XVisualInfo*)Info)->visualid);
|
|
#if GTK_CHECK_VERSION(2,91,0)
|
|
gtk_widget_set_visual(render_widget,vis);
|
|
#else
|
|
GdkColormap * cmap = gdk_colormap_new(vis, true);
|
|
gtk_widget_set_colormap(render_widget, cmap);
|
|
g_object_unref(cmap);
|
|
#endif
|
|
gtk_widget_show_all(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.
|
|
|
|
gtk_window_set_title(GTK_WINDOW(window), Title);
|
|
|
|
#if GTK_CHECK_VERSION(2,14,0)
|
|
GdkWindow* window_wnd = gtk_widget_get_window(window);
|
|
#else
|
|
GdkWindow* window_wnd = window->window;
|
|
#endif
|
|
|
|
// Wait until window is mapped to get the window's XID
|
|
gtk_widget_show_now(window);
|
|
wnd = GDK_WINDOW_XID(window_wnd);
|
|
gdk_window_add_filter(window_wnd, OnFilter, this);
|
|
|
|
XWMHints * wm_hint = XGetWMHints(dpy, wnd);
|
|
if (!wm_hint) wm_hint = XAllocWMHints();
|
|
Hints = wm_hint;
|
|
|
|
if (GTK_IS_LAYOUT(render_widget))
|
|
{
|
|
#if GTK_CHECK_VERSION(2,14,0)
|
|
GdkWindow* bin_wnd = gtk_layout_get_bin_window(GTK_LAYOUT(render_widget));
|
|
#else
|
|
GdkWindow* bin_wnd = GTK_LAYOUT(render_widget)->bin_window;
|
|
#endif
|
|
|
|
renderwnd = GDK_WINDOW_XID(bin_wnd);
|
|
}
|
|
else
|
|
{
|
|
#if GTK_CHECK_VERSION(2,14,0)
|
|
GdkWindow* render_wnd = gtk_widget_get_window(render_widget);
|
|
#else
|
|
GdkWindow* render_wnd = render_widget->window;
|
|
#endif
|
|
|
|
renderwnd = GDK_WINDOW_XID(render_wnd);
|
|
}
|
|
|
|
if (pParent) XSetTransientForHint(dpy, wnd, pParent->wnd);
|
|
|
|
if (HideCursor)
|
|
{
|
|
// TODO!
|
|
// GdkCursor* cursor = gdk_cursor_new_from_pixmap(NULL, NULL, NULL, NULL, 0, 0);
|
|
gdk_window_set_cursor(window_wnd, NULL);
|
|
}
|
|
|
|
// Make sure the window is shown and ready to be rendered into,
|
|
// this avoids an async X error.
|
|
gdk_flush();
|
|
|
|
return this;
|
|
}
|
|
|
|
bool C4GtkWindow::ReInit(C4AbstractApp* pApp)
|
|
{
|
|
// TODO: Recreate the window with a newly chosen visual
|
|
// Probably we don't need this, since there is no way to change
|
|
// MultiSampling when no window is open.
|
|
return false;
|
|
}
|
|
|
|
void C4GtkWindow::Clear()
|
|
{
|
|
if (window != NULL)
|
|
{
|
|
g_signal_handler_disconnect(window, handlerDestroy);
|
|
gtk_widget_destroy(window);
|
|
handlerDestroy = 0;
|
|
}
|
|
|
|
// Avoid that the base class tries to free these
|
|
wnd = renderwnd = 0;
|
|
|
|
window = NULL;
|
|
Active = false;
|
|
|
|
// We must free it here since we do not call C4Window::Clear()
|
|
if (Info)
|
|
{
|
|
delete static_cast<XVisualInfo*>(Info);
|
|
Info = 0;
|
|
}
|
|
}
|
|
|
|
void C4GtkWindow::OnDestroyStatic(GtkWidget* widget, gpointer data)
|
|
{
|
|
C4GtkWindow* wnd = static_cast<C4GtkWindow*>(data);
|
|
|
|
g_signal_handler_disconnect(wnd->window, wnd->handlerDestroy);
|
|
//gtk_widget_destroy(wnd->window);
|
|
wnd->handlerDestroy = 0;
|
|
wnd->window = NULL;
|
|
wnd->Active = false;
|
|
wnd->wnd = wnd->renderwnd = 0;
|
|
|
|
wnd->Close();
|
|
}
|
|
|
|
GdkFilterReturn C4GtkWindow::OnFilter(GdkXEvent* xevent, GdkEvent* event, gpointer user_data)
|
|
{
|
|
// Handle raw X message, then let GTK+ process it
|
|
static_cast<C4GtkWindow*>(user_data)->HandleMessage(*reinterpret_cast<XEvent*>(xevent));
|
|
return GDK_FILTER_CONTINUE;
|
|
}
|
|
|
|
gboolean C4GtkWindow::OnUpdateKeyMask(GtkWidget* widget, GdkEventKey* event, gpointer user_data)
|
|
{
|
|
// Update mask so that Application.IsShiftDown,
|
|
// Application.IsControlDown etc. work.
|
|
unsigned int mask = 0;
|
|
if (event->state & GDK_SHIFT_MASK) mask |= MK_SHIFT;
|
|
if (event->state & GDK_CONTROL_MASK) mask |= MK_CONTROL;
|
|
if (event->state & GDK_MOD1_MASK) mask |= (1 << 3);
|
|
|
|
// For keypress/relases, event->state contains the state _before_
|
|
// the event, but we need to store the current state.
|
|
#if !GTK_CHECK_VERSION(2,21,8)
|
|
# define GDK_KEY_Shift_L GDK_Shift_L
|
|
# define GDK_KEY_Shift_R GDK_Shift_R
|
|
# define GDK_KEY_Control_L GDK_Control_L
|
|
# define GDK_KEY_Control_R GDK_Control_R
|
|
# define GDK_KEY_Alt_L GDK_Alt_L
|
|
# define GDK_KEY_Alt_R GDK_Alt_R
|
|
#endif
|
|
|
|
if (event->keyval == GDK_KEY_Shift_L || event->keyval == GDK_KEY_Shift_R) mask ^= MK_SHIFT;
|
|
if (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R) mask ^= MK_CONTROL;
|
|
if (event->keyval == GDK_KEY_Alt_L || event->keyval == GDK_KEY_Alt_R) mask ^= (1 << 3);
|
|
|
|
static_cast<C4AbstractApp*>(user_data)->KeyMask = mask;
|
|
return false;
|
|
}
|
|
|
|
GtkWidget* C4GtkWindow::InitGUI()
|
|
{
|
|
return window;
|
|
}
|
|
|
|
void C4Window::RequestUpdate()
|
|
{
|
|
// just invoke directly
|
|
PerformUpdate();
|
|
}
|
|
|
|
bool OpenURL(const char *szURL)
|
|
{
|
|
GError *error = 0;
|
|
#if GTK_CHECK_VERSION(2,14,0)
|
|
if (gtk_show_uri(NULL, szURL, GDK_CURRENT_TIME, &error))
|
|
return true;
|
|
if (error != NULL)
|
|
{
|
|
fprintf (stderr, "Unable to open URL: %s\n", error->message);
|
|
g_error_free (error);
|
|
}
|
|
#endif
|
|
const char * argv[][3] =
|
|
{
|
|
{ "xdg-open", szURL, 0 },
|
|
{ "sensible-browser", szURL, 0 },
|
|
{ "firefox", szURL, 0 },
|
|
{ "mozilla", szURL, 0 },
|
|
{ "konqueror", szURL, 0 },
|
|
{ "epiphany", szURL, 0 },
|
|
{ 0, 0, 0 }
|
|
};
|
|
for (int i = 0; argv[i][0]; ++i)
|
|
{
|
|
error = 0;
|
|
if (g_spawn_async (g_get_home_dir(), const_cast<char**>(argv[i]), 0, G_SPAWN_SEARCH_PATH, 0, 0, 0, &error))
|
|
return true;
|
|
else
|
|
{
|
|
fprintf(stderr, "%s\n", error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
return false;
|
|
}
|