293 lines
8.0 KiB
C
293 lines
8.0 KiB
C
|
/*******************************************************************************
|
||
|
**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789
|
||
|
** 10 20 30 40 50 60 70 80
|
||
|
**
|
||
|
** program:
|
||
|
** input_shape_test
|
||
|
**
|
||
|
** created:
|
||
|
** 19.2.2006
|
||
|
**
|
||
|
** last change:
|
||
|
** 20.2.2006
|
||
|
**
|
||
|
** author:
|
||
|
** Mirco "MacSlow" Mueller <macslow@bangang.de>
|
||
|
**
|
||
|
** license:
|
||
|
** GPL
|
||
|
**
|
||
|
** notes:
|
||
|
** - this is a test I did to figure out how to use input-shapes (XShape 1.1)
|
||
|
** - opens a decoration-less and transparent gtk+-window and draws a red
|
||
|
** cross with a circle around it
|
||
|
** - only the parts drawn will be "draggable"
|
||
|
** - window can be dragged around with LMB-drag
|
||
|
** - window can be resized with MMB-drag (the input-shape also resizes!)
|
||
|
** - window can be exited with ESC or q
|
||
|
** - needs a compositing manager to run in order to look as intended
|
||
|
** - tested with xcompmgr and compiz
|
||
|
** - tested with gtk+-2.8.11 and gtk+-2.9.0 (CVS-head)
|
||
|
**
|
||
|
** bugs:
|
||
|
** - there are no size-checks done for the input-shape, so I don't know what
|
||
|
** will happen, if you make the window super large
|
||
|
**
|
||
|
** todo:
|
||
|
** - nothing
|
||
|
**
|
||
|
** compile with:
|
||
|
** gcc `pkg-config --cflags --libs gtk+-2.0` input_shape_test.c -o input_shape_test
|
||
|
**
|
||
|
*******************************************************************************/
|
||
|
|
||
|
#include <math.h>
|
||
|
#include <gdk/gdkkeysyms.h>
|
||
|
#include <gtk/gtk.h>
|
||
|
#if !GTK_CHECK_VERSION(2,9,0)
|
||
|
#include <X11/Xlib.h>
|
||
|
#include <X11/extensions/shape.h>
|
||
|
#include <gdk/gdkx.h>
|
||
|
#endif
|
||
|
|
||
|
#define WIN_WIDTH 300
|
||
|
#define WIN_HEIGHT 300
|
||
|
|
||
|
gint g_iCurrentWidth = WIN_WIDTH;
|
||
|
gint g_iCurrentHeight = WIN_HEIGHT;
|
||
|
|
||
|
void update_input_shape (GtkWidget* pWindow, int iWidth, int iHeight);
|
||
|
void on_alpha_screen_changed (GtkWidget* pWidget, GdkScreen* pOldScreen, GtkWidget* pLabel);
|
||
|
void render (cairo_t* pCairoContext, gint iWidth, gint iHeight);
|
||
|
gboolean on_expose (GtkWidget* pWidget, GdkEventExpose* pExpose);
|
||
|
gboolean on_key_press (GtkWidget* pWidget, GdkEventKey* pKey, gpointer userData);
|
||
|
gboolean on_button_press (GtkWidget* pWidget, GdkEventButton* pButton, GdkWindowEdge edge);
|
||
|
gboolean on_configure (GtkWidget* pWidget, GdkEventConfigure* pEvent, gpointer data);
|
||
|
#if !GTK_CHECK_VERSION(2,9,0)
|
||
|
void do_shape_combine_mask (GdkWindow* window, GdkBitmap* mask, gint x, gint y);
|
||
|
#endif
|
||
|
void update_input_shape (GtkWidget* pWindow, int iWidth, int iHeight);
|
||
|
|
||
|
void on_alpha_screen_changed (GtkWidget* pWidget,
|
||
|
GdkScreen* pOldScreen,
|
||
|
GtkWidget* pLabel)
|
||
|
{
|
||
|
GdkScreen* pScreen = gtk_widget_get_screen (pWidget);
|
||
|
GdkColormap* pColormap = gdk_screen_get_rgba_colormap (pScreen);
|
||
|
|
||
|
if (!pColormap)
|
||
|
pColormap = gdk_screen_get_rgb_colormap (pScreen);
|
||
|
|
||
|
gtk_widget_set_colormap (pWidget, pColormap);
|
||
|
}
|
||
|
|
||
|
void render (cairo_t* pCairoContext, gint iWidth, gint iHeight)
|
||
|
{
|
||
|
cairo_scale (pCairoContext, (double) iWidth, (double) iHeight);
|
||
|
cairo_set_source_rgba (pCairoContext, 1.0f, 1.0f, 1.0f, 0.0f);
|
||
|
cairo_set_operator (pCairoContext, CAIRO_OPERATOR_SOURCE);
|
||
|
cairo_paint (pCairoContext);
|
||
|
cairo_set_source_rgba (pCairoContext, 1.0f, 0.0f, 0.0f, 0.75f);
|
||
|
cairo_set_line_width (pCairoContext, 0.1f);
|
||
|
cairo_move_to (pCairoContext, 0.15f, 0.15f);
|
||
|
cairo_line_to (pCairoContext, 0.85f, 0.85f);
|
||
|
cairo_move_to (pCairoContext, 0.85f, 0.15f);
|
||
|
cairo_line_to (pCairoContext, 0.15f, 0.85f);
|
||
|
cairo_stroke (pCairoContext);
|
||
|
cairo_arc (pCairoContext, 0.5f, 0.5f, 0.45f, 0.0f, M_PI/180.0f * 360.0f);
|
||
|
cairo_stroke (pCairoContext);
|
||
|
}
|
||
|
|
||
|
gboolean on_expose (GtkWidget* pWidget,
|
||
|
GdkEventExpose* pExpose)
|
||
|
{
|
||
|
gint iWidth;
|
||
|
gint iHeight;
|
||
|
cairo_t* pCairoContext = NULL;
|
||
|
|
||
|
pCairoContext = gdk_cairo_create (pWidget->window);
|
||
|
if (!pCairoContext)
|
||
|
return FALSE;
|
||
|
|
||
|
gtk_window_get_size (GTK_WINDOW (pWidget), &iWidth, &iHeight);
|
||
|
render (pCairoContext, iWidth, iHeight);
|
||
|
cairo_destroy (pCairoContext);
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
gboolean on_configure (GtkWidget* pWidget,
|
||
|
GdkEventConfigure* pEvent,
|
||
|
gpointer userData)
|
||
|
{
|
||
|
gint iNewWidth = pEvent->width;
|
||
|
gint iNewHeight = pEvent->height;
|
||
|
|
||
|
if (iNewWidth != g_iCurrentWidth || iNewHeight != g_iCurrentHeight)
|
||
|
{
|
||
|
update_input_shape (pWidget, iNewWidth, iNewHeight);
|
||
|
g_iCurrentWidth = iNewWidth;
|
||
|
g_iCurrentHeight = iNewHeight;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
gboolean on_key_press (GtkWidget* pWidget,
|
||
|
GdkEventKey* pKey,
|
||
|
gpointer userData)
|
||
|
{
|
||
|
gint iWidth;
|
||
|
gint iHeight;
|
||
|
|
||
|
if (pKey->type == GDK_KEY_PRESS)
|
||
|
{
|
||
|
switch (pKey->keyval)
|
||
|
{
|
||
|
case GDK_Escape :
|
||
|
case GDK_q :
|
||
|
gtk_main_quit ();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
gboolean on_button_press (GtkWidget* pWidget,
|
||
|
GdkEventButton* pButton,
|
||
|
GdkWindowEdge edge)
|
||
|
{
|
||
|
if (pButton->type == GDK_BUTTON_PRESS)
|
||
|
{
|
||
|
if (pButton->button == 1)
|
||
|
gtk_window_begin_move_drag (GTK_WINDOW (gtk_widget_get_toplevel (pWidget)),
|
||
|
pButton->button,
|
||
|
pButton->x_root,
|
||
|
pButton->y_root,
|
||
|
pButton->time);
|
||
|
if (pButton->button == 2)
|
||
|
gtk_window_begin_resize_drag (GTK_WINDOW (gtk_widget_get_toplevel (pWidget)),
|
||
|
edge,
|
||
|
pButton->button,
|
||
|
pButton->x_root,
|
||
|
pButton->y_root,
|
||
|
pButton->time);
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
#if !GTK_CHECK_VERSION(2,9,0)
|
||
|
/* this is piece by piece taken from gtk+ 2.9.0 (CVS-head with a patch applied
|
||
|
regarding XShape's input-masks) so people without gtk+ >= 2.9.0 can compile and
|
||
|
run input_shape_test.c */
|
||
|
void do_shape_combine_mask (GdkWindow* window,
|
||
|
GdkBitmap* mask,
|
||
|
gint x,
|
||
|
gint y)
|
||
|
{
|
||
|
Pixmap pixmap;
|
||
|
int ignore;
|
||
|
int maj;
|
||
|
int min;
|
||
|
|
||
|
if (!XShapeQueryExtension (GDK_WINDOW_XDISPLAY (window), &ignore, &ignore))
|
||
|
return;
|
||
|
|
||
|
if (!XShapeQueryVersion (GDK_WINDOW_XDISPLAY (window), &maj, &min))
|
||
|
return;
|
||
|
|
||
|
/* for shaped input we need at least XShape 1.1 */
|
||
|
if (maj != 1 && min < 1)
|
||
|
return;
|
||
|
|
||
|
if (mask)
|
||
|
pixmap = GDK_DRAWABLE_XID (mask);
|
||
|
else
|
||
|
{
|
||
|
x = 0;
|
||
|
y = 0;
|
||
|
pixmap = None;
|
||
|
}
|
||
|
|
||
|
XShapeCombineMask (GDK_WINDOW_XDISPLAY (window),
|
||
|
GDK_DRAWABLE_XID (window),
|
||
|
ShapeInput,
|
||
|
x,
|
||
|
y,
|
||
|
pixmap,
|
||
|
ShapeSet);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void update_input_shape (GtkWidget* pWindow, int iWidth, int iHeight)
|
||
|
{
|
||
|
static GdkBitmap* pShapeBitmap = NULL;
|
||
|
static cairo_t* pCairoContext = NULL;
|
||
|
|
||
|
pShapeBitmap = (GdkBitmap*) gdk_pixmap_new (NULL, iWidth, iHeight, 1);
|
||
|
if (pShapeBitmap)
|
||
|
{
|
||
|
pCairoContext = gdk_cairo_create (pShapeBitmap);
|
||
|
if (cairo_status (pCairoContext) == CAIRO_STATUS_SUCCESS)
|
||
|
{
|
||
|
render (pCairoContext, iWidth, iHeight);
|
||
|
cairo_destroy (pCairoContext);
|
||
|
#if !GTK_CHECK_VERSION(2,9,0)
|
||
|
do_shape_combine_mask (pWindow->window, NULL, 0, 0);
|
||
|
do_shape_combine_mask (pWindow->window, pShapeBitmap, 0, 0);
|
||
|
#else
|
||
|
gtk_widget_input_shape_combine_mask (pWindow, NULL, 0, 0);
|
||
|
gtk_widget_input_shape_combine_mask (pWindow, pShapeBitmap, 0, 0);
|
||
|
#endif
|
||
|
}
|
||
|
g_object_unref ((gpointer) pShapeBitmap);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int main (int argc, char** argv)
|
||
|
{
|
||
|
GtkWidget* pWindow = NULL;
|
||
|
GdkBitmap* pShapeMaskBitmap = NULL;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
pWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
on_alpha_screen_changed (pWindow, NULL, NULL);
|
||
|
gtk_widget_set_app_paintable (pWindow, TRUE);
|
||
|
gtk_window_set_decorated (GTK_WINDOW (pWindow), FALSE);
|
||
|
gtk_window_set_resizable (GTK_WINDOW (pWindow), TRUE);
|
||
|
gtk_window_set_title (GTK_WINDOW (pWindow), "gtk+/XShape 1.1 test");
|
||
|
gtk_widget_set_size_request (pWindow, g_iCurrentWidth, g_iCurrentHeight);
|
||
|
gtk_widget_add_events (pWindow, GDK_BUTTON_PRESS_MASK);
|
||
|
gtk_widget_show (pWindow);
|
||
|
|
||
|
g_signal_connect (G_OBJECT (pWindow),
|
||
|
"destroy",
|
||
|
G_CALLBACK (gtk_main_quit),
|
||
|
NULL);
|
||
|
g_signal_connect (G_OBJECT (pWindow),
|
||
|
"expose-event",
|
||
|
G_CALLBACK (on_expose),
|
||
|
NULL);
|
||
|
g_signal_connect (G_OBJECT (pWindow),
|
||
|
"configure-event",
|
||
|
G_CALLBACK (on_configure),
|
||
|
NULL);
|
||
|
g_signal_connect (G_OBJECT (pWindow),
|
||
|
"key-press-event",
|
||
|
G_CALLBACK (on_key_press),
|
||
|
NULL);
|
||
|
g_signal_connect (G_OBJECT (pWindow),
|
||
|
"button-press-event",
|
||
|
G_CALLBACK (on_button_press),
|
||
|
NULL);
|
||
|
|
||
|
update_input_shape (pWindow, g_iCurrentWidth, g_iCurrentHeight);
|
||
|
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|