Finished implementing mouse events on GTK+ uiArea.
This commit is contained in:
parent
3d80f8b11f
commit
018c18a74d
|
@ -175,7 +175,7 @@ static uiModifiers toModifiers(guint state)
|
||||||
uiModifiers m;
|
uiModifiers m;
|
||||||
|
|
||||||
m = 0;
|
m = 0;
|
||||||
if ((state & GDK_CONTROL_MASK) != 0))
|
if ((state & GDK_CONTROL_MASK) != 0)
|
||||||
m |= uiModifierCtrl;
|
m |= uiModifierCtrl;
|
||||||
if ((state & GDK_META_MASK) != 0)
|
if ((state & GDK_META_MASK) != 0)
|
||||||
m |= uiModifierAlt;
|
m |= uiModifierAlt;
|
||||||
|
@ -280,7 +280,7 @@ static gboolean areaWidget_motion_notify_event(GtkWidget *w, GdkEventMotion *e)
|
||||||
// we want switching away from the control to reset the double-click counter, like with WM_ACTIVATE on Windows
|
// we want switching away from the control to reset the double-click counter, like with WM_ACTIVATE on Windows
|
||||||
// according to tristan in irc.gimp.net/#gtk+, doing this on enter-notify-event and leave-notify-event is correct (and it seems to be true in my own tests; plus the events DO get sent when switching programs with the keyboard (just pointing that out))
|
// according to tristan in irc.gimp.net/#gtk+, doing this on enter-notify-event and leave-notify-event is correct (and it seems to be true in my own tests; plus the events DO get sent when switching programs with the keyboard (just pointing that out))
|
||||||
// differentiating between enter-notify-event and leave-notify-event is unimportant
|
// differentiating between enter-notify-event and leave-notify-event is unimportant
|
||||||
gboolean areaWidget_enterleave_notify_event(GtkWidget *widget, GdkEventCrossing *e)
|
gboolean areaWidget_enterleave_notify_event(GtkWidget *w, GdkEventCrossing *e)
|
||||||
{
|
{
|
||||||
struct areaPrivate *ap = areaWidget(w)->priv;
|
struct areaPrivate *ap = areaWidget(w)->priv;
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ struct areaWidgetClass {
|
||||||
extern GType areaWidget_get_type(void);
|
extern GType areaWidget_get_type(void);
|
||||||
|
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "uipriv.h"
|
||||||
|
|
||||||
extern GtkWidget *newArea(uiAreaHandler *ah);
|
extern GtkWidget *newArea(uiAreaHandler *ah);
|
||||||
extern void areaUpdateScroll(GtkWidget *area);
|
extern void areaUpdateScroll(GtkWidget *area);
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
// 29 march 2014
|
||||||
|
// TODO remove
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "ui.h"
|
||||||
|
#include "uipriv.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Windows and GTK+ have a limit of 2 and 3 clicks, respectively, natively supported. Fortunately, we can simulate the double/triple-click behavior to build higher-order clicks. We can use the same algorithm Windows uses on both:
|
||||||
|
http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
|
||||||
|
For GTK+, we pull the double-click time and double-click distance, which work the same as the equivalents on Windows (so the distance is in all directions), from the GtkSettings system.
|
||||||
|
|
||||||
|
On GTK+ this will also allow us to discard the GDK_BUTTON_2PRESS and GDK_BUTTON_3PRESS events, so the button press stream will be just like on other platforms.
|
||||||
|
|
||||||
|
Thanks to mclasen, garnacho_, halfline, and tristan in irc.gimp.net/#gtk+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// x, y, xdist, ydist, and c.rect must have the same units
|
||||||
|
// so must time, maxTime, and c.prevTime
|
||||||
|
uintmax_t clickCounterClick(clickCounter *c, uintmax_t button, intmax_t x, intmax_t y, uintptr_t time, uintptr_t maxTime, intmax_t xdist, intmax_t ydist)
|
||||||
|
{
|
||||||
|
// different button than before? if so, don't count
|
||||||
|
if (button != c->curButton)
|
||||||
|
c->count = 0;
|
||||||
|
|
||||||
|
// (x, y) in the allowed region for a double-click? if not, don't count
|
||||||
|
if (x < c->rectX0)
|
||||||
|
c->count = 0;
|
||||||
|
if (y < c->rectY0)
|
||||||
|
c->count = 0;
|
||||||
|
if (x >= c->rectX1)
|
||||||
|
c->count = 0;
|
||||||
|
if (y >= c->rectY1)
|
||||||
|
c->count = 0;
|
||||||
|
|
||||||
|
// too slow? if so, don't count
|
||||||
|
// note the below expression; time > (c.prevTime + maxTime) can overflow!
|
||||||
|
if ((time - c->prevTime) > maxTime) // too slow; don't count
|
||||||
|
c->count = 0;
|
||||||
|
|
||||||
|
c->count++; // if either of the above ifs happened, this will make the click count 1; otherwise it will make the click count 2, 3, 4, 5, ...
|
||||||
|
|
||||||
|
// now we need to update the internal structures for the next test
|
||||||
|
c->curButton = button;
|
||||||
|
c->prevTime = time;
|
||||||
|
c->rectX0 = x - xdist;
|
||||||
|
c->rectY0 = y - ydist;
|
||||||
|
c->rectX1 = x + xdist;
|
||||||
|
c->rectY1 = y + ydist;
|
||||||
|
|
||||||
|
return c->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clickCounterReset(clickCounter *c)
|
||||||
|
{
|
||||||
|
c->curButton = 0;
|
||||||
|
c->rectX0 = 0;
|
||||||
|
c->rectY0 = 0;
|
||||||
|
c->rectX1 = 0;
|
||||||
|
c->rectY1 = 0;
|
||||||
|
c->prevTime = 0;
|
||||||
|
c->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
For position independence across international keyboard layouts, typewriter keys are read using scancodes (which are always set 1).
|
||||||
|
Windows provides the scancodes directly in the LPARAM.
|
||||||
|
GTK+ provides the scancodes directly from the underlying window system via GdkEventKey.hardware_keycode.
|
||||||
|
On X11, this is scancode + 8 (because X11 keyboard codes have a range of [8,255]).
|
||||||
|
Wayland is guaranteed to give the same result (thanks ebassi in irc.gimp.net/#gtk+).
|
||||||
|
On Linux, where evdev is used instead of polling scancodes directly from the keyboard, evdev's typewriter section key code constants are the same as scancodes anyway, so the rules above apply.
|
||||||
|
Typewriter section scancodes are the same across international keyboards with some exceptions that have been accounted for (see KeyEvent's documentation); see http://www.quadibloc.com/comp/scan.htm for details.
|
||||||
|
Non-typewriter keys can be handled safely using constants provided by the respective backend API.
|
||||||
|
|
||||||
|
Because GTK+ keysyms may or may not obey Num Lock, we also handle the 0-9 and . keys on the numeric keypad with scancodes (they match too).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
|
|
||||||
|
// use uintptr to be safe; the size of the scancode/hardware key code field on each platform is different
|
||||||
|
var scancodeKeys = map[uintptr]byte{
|
||||||
|
0x02: '1',
|
||||||
|
0x03: '2',
|
||||||
|
0x04: '3',
|
||||||
|
0x05: '4',
|
||||||
|
0x06: '5',
|
||||||
|
0x07: '6',
|
||||||
|
0x08: '7',
|
||||||
|
0x09: '8',
|
||||||
|
0x0A: '9',
|
||||||
|
0x0B: '0',
|
||||||
|
0x0C: '-',
|
||||||
|
0x0D: '=',
|
||||||
|
0x0E: '\b',
|
||||||
|
0x0F: '\t',
|
||||||
|
0x10: 'q',
|
||||||
|
0x11: 'w',
|
||||||
|
0x12: 'e',
|
||||||
|
0x13: 'r',
|
||||||
|
0x14: 't',
|
||||||
|
0x15: 'y',
|
||||||
|
0x16: 'u',
|
||||||
|
0x17: 'i',
|
||||||
|
0x18: 'o',
|
||||||
|
0x19: 'p',
|
||||||
|
0x1A: '[',
|
||||||
|
0x1B: ']',
|
||||||
|
0x1C: '\n',
|
||||||
|
0x1E: 'a',
|
||||||
|
0x1F: 's',
|
||||||
|
0x20: 'd',
|
||||||
|
0x21: 'f',
|
||||||
|
0x22: 'g',
|
||||||
|
0x23: 'h',
|
||||||
|
0x24: 'j',
|
||||||
|
0x25: 'k',
|
||||||
|
0x26: 'l',
|
||||||
|
0x27: ';',
|
||||||
|
0x28: '\'',
|
||||||
|
0x29: '`',
|
||||||
|
0x2B: '\\',
|
||||||
|
0x2C: 'z',
|
||||||
|
0x2D: 'x',
|
||||||
|
0x2E: 'c',
|
||||||
|
0x2F: 'v',
|
||||||
|
0x30: 'b',
|
||||||
|
0x31: 'n',
|
||||||
|
0x32: 'm',
|
||||||
|
0x33: ',',
|
||||||
|
0x34: '.',
|
||||||
|
0x35: '/',
|
||||||
|
0x39: ' ',
|
||||||
|
}
|
||||||
|
|
||||||
|
var scancodeExtKeys = map[uintptr]ExtKey{
|
||||||
|
0x47: N7,
|
||||||
|
0x48: N8,
|
||||||
|
0x49: N9,
|
||||||
|
0x4B: N4,
|
||||||
|
0x4C: N5,
|
||||||
|
0x4D: N6,
|
||||||
|
0x4F: N1,
|
||||||
|
0x50: N2,
|
||||||
|
0x51: N3,
|
||||||
|
0x52: N0,
|
||||||
|
0x53: NDot,
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromScancode(scancode uintptr) (ke KeyEvent, ok bool) {
|
||||||
|
if key, ok := scancodeKeys[scancode]; ok {
|
||||||
|
ke.Key = key
|
||||||
|
return ke, true
|
||||||
|
}
|
||||||
|
if extkey, ok := scancodeExtKeys[scancode]; ok {
|
||||||
|
ke.ExtKey = extkey
|
||||||
|
return ke, true
|
||||||
|
}
|
||||||
|
return ke, false
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
|
@ -104,6 +104,20 @@ static int handlerRedrawOnResize(uiAreaHandler *a, uiArea *area)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e)
|
||||||
|
{
|
||||||
|
printf("mouse (%d,%d):(%d,%d) dn:%d up:%d count:%d mods:%x held:%x\n",
|
||||||
|
(int) e->X,
|
||||||
|
(int) e->Y,
|
||||||
|
(int) e->HScrollPos,
|
||||||
|
(int) e->VScrollPos,
|
||||||
|
(int) e->Down,
|
||||||
|
(int) e->Up,
|
||||||
|
(int) e->Count,
|
||||||
|
(uint32_t) e->Modifiers,
|
||||||
|
e->Held1To64);
|
||||||
|
}
|
||||||
|
|
||||||
static void recalcScroll(GtkSpinButton *sb, gpointer data)
|
static void recalcScroll(GtkSpinButton *sb, gpointer data)
|
||||||
{
|
{
|
||||||
areaUpdateScroll(area);
|
areaUpdateScroll(area);
|
||||||
|
@ -130,6 +144,7 @@ int main(void)
|
||||||
h.ah.HScrollMax = handlerHScrollMax;
|
h.ah.HScrollMax = handlerHScrollMax;
|
||||||
h.ah.VScrollMax = handlerVScrollMax;
|
h.ah.VScrollMax = handlerVScrollMax;
|
||||||
h.ah.RedrawOnResize = handlerRedrawOnResize;
|
h.ah.RedrawOnResize = handlerRedrawOnResize;
|
||||||
|
h.ah.MouseEvent = handlerMouseEvent;
|
||||||
|
|
||||||
gtk_init(NULL, NULL);
|
gtk_init(NULL, NULL);
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,8 @@ struct uiAreaMouseEvent {
|
||||||
uintmax_t Down;
|
uintmax_t Down;
|
||||||
uintmax_t Up;
|
uintmax_t Up;
|
||||||
|
|
||||||
|
uintmax_t Count;
|
||||||
|
|
||||||
uiModifiers Modifiers;
|
uiModifiers Modifiers;
|
||||||
|
|
||||||
uint64_t Held1To64;
|
uint64_t Held1To64;
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
typedef struct clickCounter clickCounter;
|
||||||
|
// you should call Reset() to zero-initialize a new instance
|
||||||
|
// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly
|
||||||
|
struct clickCounter {
|
||||||
|
uintmax_t curButton;
|
||||||
|
intmax_t rectX0;
|
||||||
|
intmax_t rectY0;
|
||||||
|
intmax_t rectX1;
|
||||||
|
intmax_t rectY1;
|
||||||
|
uintptr_t prevTime;
|
||||||
|
uintmax_t count;
|
||||||
|
};
|
||||||
|
extern uintmax_t clickCounterClick(clickCounter *, uintmax_t, intmax_t, intmax_t, uintptr_t, uintptr_t, intmax_t, intmax_t);
|
||||||
|
extern void clickCounterReset(clickCounter *);
|
Loading…
Reference in New Issue