This commit creates a new "wayland" backend, which can be activated at configure time with "-DFREEGLUT_WAYLAND=ON". If done so, it will be used instead of X11 (building both and doing runtime detection may become possible later). Please note that if you choose to use GL instead of GLES (by not specifying "-DFREEGLUT_GLES=ON"), then libX11 will still be pulled as an indirect dependency. Following features are still WIP : - menus (not implemented, TODO) ; - client-side decorations (not implemented, required because Wayland shells do not draw title bars nor resize grips, TODO) ; - game mode (code is commented out, depends on WIP protocols tested upstream, WAIT FOR UPSTREAM) ; - window visibility states (code is commented out, depends on xdg-shell protocol, TODO). Signed-off-by: Manuel Bachmann <tarnyko@tarnyko.net> git-svn-id: https://svn.code.sf.net/p/freeglut/code/trunk@1758 7f0cb862-5218-0410-a997-914c9d46530a
434 lines
15 KiB
C
434 lines
15 KiB
C
/*
|
|
* fg_input_devices_wl.c
|
|
*
|
|
* Handles Wayland input devices : keyboard, pointer, touchscreen.
|
|
*
|
|
* Written by Manuel Bachmann <tarnyko@tarnyko.net> 2015
|
|
*
|
|
* Copyright (c) 2015 Manuel Bachmann. All Rights Reserved.
|
|
* Creation date: Thur Mar 19 2015
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* MANUEL BACHMANN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#ifdef __linux__
|
|
#include <linux/input.h>
|
|
#else
|
|
#define BTN_LEFT 0x110
|
|
#define BTN_RIGHT 0x111
|
|
#define BTN_MIDDLE 0x112
|
|
#endif
|
|
#include <sys/mman.h>
|
|
#include <GL/freeglut.h>
|
|
#include "../fg_internal.h"
|
|
|
|
/*
|
|
* This function will effectively set the pointer (mouse) cursor
|
|
* depending on the GLUT_CURSOR_* choice.
|
|
*/
|
|
void fghPointerSetCursor( SFG_Window* window,
|
|
struct wl_pointer* pointer,
|
|
uint32_t serial )
|
|
{
|
|
struct wl_cursor_image* image;
|
|
struct wl_buffer* buffer;
|
|
|
|
image = window->Window.pContext.cursor->images[0];
|
|
buffer = wl_cursor_image_get_buffer( image );
|
|
|
|
wl_surface_attach( window->Window.pContext.cursor_surface, buffer,
|
|
0, 0 );
|
|
wl_surface_damage( window->Window.pContext.cursor_surface, 0, 0,
|
|
image->width, image->height );
|
|
wl_surface_commit( window->Window.pContext.cursor_surface );
|
|
|
|
wl_pointer_set_cursor( pointer, serial,
|
|
window->Window.pContext.cursor_surface,
|
|
image->hotspot_x, image->hotspot_y );
|
|
}
|
|
|
|
/*
|
|
* This function will interpret a keyboard keysym, and call the
|
|
* possible callbacks accordingly.
|
|
*/
|
|
void fghKeyboardInterpretKeysym( SFG_Window* window,
|
|
uint32_t key,
|
|
xkb_keysym_t sym,
|
|
uint32_t state )
|
|
{
|
|
FGCBKeyboard keyboard_cb;
|
|
FGCBSpecial special_cb;
|
|
char string[16];
|
|
int special = -1;
|
|
|
|
/* GLUT API tells us to have two separate callbacks, one for
|
|
* the ASCII translateable keypresses, and one for all the
|
|
* others, which need to be translated to GLUT_KEY_Xs... */
|
|
if( state )
|
|
{
|
|
keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
|
|
special_cb = (FGCBSpecial) ( FETCH_WCB( *window, Special ));
|
|
}
|
|
else
|
|
{
|
|
keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
|
|
special_cb = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp ));
|
|
}
|
|
|
|
switch( sym )
|
|
{
|
|
case XKB_KEY_F1: special = GLUT_KEY_F1; break;
|
|
case XKB_KEY_F2: special = GLUT_KEY_F2; break;
|
|
case XKB_KEY_F3: special = GLUT_KEY_F3; break;
|
|
case XKB_KEY_F4: special = GLUT_KEY_F4; break;
|
|
case XKB_KEY_F5: special = GLUT_KEY_F5; break;
|
|
case XKB_KEY_F6: special = GLUT_KEY_F6; break;
|
|
case XKB_KEY_F7: special = GLUT_KEY_F7; break;
|
|
case XKB_KEY_F8: special = GLUT_KEY_F8; break;
|
|
case XKB_KEY_F9: special = GLUT_KEY_F9; break;
|
|
case XKB_KEY_F10: special = GLUT_KEY_F10; break;
|
|
case XKB_KEY_F11: special = GLUT_KEY_F11; break;
|
|
case XKB_KEY_F12: special = GLUT_KEY_F12; break;
|
|
case XKB_KEY_Left: special = GLUT_KEY_LEFT; break;
|
|
case XKB_KEY_Right: special = GLUT_KEY_RIGHT; break;
|
|
case XKB_KEY_Up: special = GLUT_KEY_UP; break;
|
|
case XKB_KEY_Down: special = GLUT_KEY_DOWN; break;
|
|
case XKB_KEY_Page_Up: special = GLUT_KEY_PAGE_UP; break;
|
|
case XKB_KEY_Page_Down: special = GLUT_KEY_PAGE_DOWN; break;
|
|
case XKB_KEY_Home: special = GLUT_KEY_HOME; break;
|
|
case XKB_KEY_End: special = GLUT_KEY_END; break;
|
|
case XKB_KEY_Insert: special = GLUT_KEY_INSERT; break;
|
|
case XKB_KEY_Num_Lock: special = GLUT_KEY_NUM_LOCK; break;
|
|
case XKB_KEY_Begin: special = GLUT_KEY_BEGIN; break;
|
|
case XKB_KEY_Delete: special = GLUT_KEY_DELETE; break;
|
|
case XKB_KEY_Shift_L: special = GLUT_KEY_SHIFT_L; break;
|
|
case XKB_KEY_Shift_R: special = GLUT_KEY_SHIFT_R; break;
|
|
case XKB_KEY_Control_L: special = GLUT_KEY_CTRL_L; break;
|
|
case XKB_KEY_Control_R: special = GLUT_KEY_CTRL_R; break;
|
|
case XKB_KEY_Alt_L: special = GLUT_KEY_ALT_L; break;
|
|
case XKB_KEY_Alt_R: special = GLUT_KEY_ALT_R; break;
|
|
}
|
|
|
|
if( special_cb && (special != -1) )
|
|
{
|
|
fgSetWindow( window );
|
|
special_cb( special, window->State.MouseX, window->State.MouseY );
|
|
}
|
|
else if( keyboard_cb && (special == -1) )
|
|
{
|
|
fgSetWindow( window );
|
|
xkb_keysym_to_utf8( sym, string, sizeof( string ) );
|
|
keyboard_cb( string[0], window->State.MouseX, window->State.MouseY );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Touchscreen section
|
|
* For now, let us pretend it is a mouse with only one button
|
|
*/
|
|
static void fghTouchDown( void* data, struct wl_touch* touch,
|
|
uint32_t serial, uint32_t time,
|
|
struct wl_surface* surface,
|
|
int32_t id,
|
|
wl_fixed_t x_w, wl_fixed_t y_w )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
win->State.MouseX = wl_fixed_to_int( x_w );
|
|
win->State.MouseY = wl_fixed_to_int( y_w );
|
|
INVOKE_WCB( *win, Mouse, ( GLUT_LEFT_BUTTON,
|
|
GLUT_DOWN,
|
|
win->State.MouseX,
|
|
win->State.MouseY ) );
|
|
}
|
|
static void fghTouchUp( void* data, struct wl_touch* touch,
|
|
uint32_t serial, uint32_t time,
|
|
int32_t id )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
INVOKE_WCB( *win, Mouse, ( GLUT_LEFT_BUTTON,
|
|
GLUT_UP,
|
|
win->State.MouseX,
|
|
win->State.MouseY ) );
|
|
}
|
|
static void fghTouchMotion( void* data, struct wl_touch* touch,
|
|
uint32_t time, int32_t id,
|
|
wl_fixed_t x_w, wl_fixed_t y_w )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
win->State.MouseX = wl_fixed_to_int( x_w );
|
|
win->State.MouseY = wl_fixed_to_int( y_w );
|
|
INVOKE_WCB( *win, Motion, ( win->State.MouseX,
|
|
win->State.MouseY ) );
|
|
}
|
|
static void fghTouchFrame( void* data, struct wl_touch* touch )
|
|
{
|
|
}
|
|
static void fghTouchCancel( void* data, struct wl_touch* touch )
|
|
{
|
|
}
|
|
static const struct wl_touch_listener fghTouchListener =
|
|
{
|
|
fghTouchDown,
|
|
fghTouchUp,
|
|
fghTouchMotion,
|
|
fghTouchFrame,
|
|
fghTouchCancel
|
|
};
|
|
|
|
|
|
/*
|
|
* Pointer (mouse) section
|
|
*/
|
|
static void fghPointerEnter( void* data, struct wl_pointer* pointer,
|
|
uint32_t serial,
|
|
struct wl_surface* surface,
|
|
wl_fixed_t x_w, wl_fixed_t y_w )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
fghPointerSetCursor( win, pointer, serial );
|
|
win->State.MouseX = wl_fixed_to_int( x_w );
|
|
win->State.MouseY = wl_fixed_to_int( y_w );
|
|
INVOKE_WCB( *win, Entry, ( GLUT_ENTERED ) );
|
|
}
|
|
static void fghPointerLeave( void* data, struct wl_pointer* pointer,
|
|
uint32_t serial,
|
|
struct wl_surface* surface )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
INVOKE_WCB( *win, Entry, ( GLUT_LEFT ) );
|
|
}
|
|
static void fghPointerMotion( void* data, struct wl_pointer* pointer,
|
|
uint32_t time,
|
|
wl_fixed_t x_w, wl_fixed_t y_w )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
win->State.MouseX = wl_fixed_to_int( x_w );
|
|
win->State.MouseY = wl_fixed_to_int( y_w );
|
|
|
|
if ( win->Window.pContext.pointer_button_pressed )
|
|
INVOKE_WCB( *win, Motion, ( win->State.MouseX,
|
|
win->State.MouseY ) );
|
|
else
|
|
INVOKE_WCB( *win, Passive, ( win->State.MouseX,
|
|
win->State.MouseY ) );
|
|
}
|
|
static void fghPointerButton( void* data, struct wl_pointer* pointer,
|
|
uint32_t serial, uint32_t time,
|
|
uint32_t button, uint32_t state )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
int button_f;
|
|
|
|
switch( button )
|
|
{
|
|
case BTN_LEFT:
|
|
button_f = GLUT_LEFT_BUTTON;
|
|
break;
|
|
case BTN_RIGHT:
|
|
button_f = GLUT_RIGHT_BUTTON;
|
|
break;
|
|
case BTN_MIDDLE:
|
|
button_f = GLUT_MIDDLE_BUTTON;
|
|
break;
|
|
}
|
|
|
|
win->Window.pContext.pointer_button_pressed =
|
|
state ? GL_TRUE : GL_FALSE;
|
|
|
|
INVOKE_WCB( *win, Mouse, ( button_f,
|
|
state ? GLUT_DOWN : GLUT_UP ,
|
|
win->State.MouseX,
|
|
win->State.MouseY ) );
|
|
}
|
|
static void fghPointerAxis( void* data, struct wl_pointer* pointer,
|
|
uint32_t time, uint32_t axis,
|
|
wl_fixed_t value )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
int direction = wl_fixed_to_int( value );
|
|
|
|
INVOKE_WCB( *win, MouseWheel, ( 0,
|
|
direction ,
|
|
win->State.MouseX,
|
|
win->State.MouseY ) );
|
|
}
|
|
static const struct wl_pointer_listener fghPointerListener =
|
|
{
|
|
fghPointerEnter,
|
|
fghPointerLeave,
|
|
fghPointerMotion,
|
|
fghPointerButton,
|
|
fghPointerAxis
|
|
};
|
|
|
|
|
|
/*
|
|
* Keyboard section
|
|
*/
|
|
static void fghKeyboardKeymap( void* data, struct wl_keyboard* keyboard,
|
|
uint32_t format, int fd, uint32_t size )
|
|
{
|
|
SFG_PlatformDisplay* pDisplay = data;
|
|
char* keymap_str;
|
|
struct xkb_keymap* keymap;
|
|
|
|
keymap_str = mmap( NULL, size, PROT_READ, MAP_SHARED, fd, 0 );
|
|
keymap = xkb_keymap_new_from_string( pDisplay->xkb_context,
|
|
keymap_str,
|
|
XKB_KEYMAP_FORMAT_TEXT_V1,
|
|
0 );
|
|
munmap( keymap_str, size );
|
|
|
|
if( pDisplay->xkb_state )
|
|
xkb_state_unref( pDisplay->xkb_state );
|
|
pDisplay->xkb_state = xkb_state_new( keymap );
|
|
}
|
|
static void fghKeyboardEnter( void* data, struct wl_keyboard* keyboard,
|
|
uint32_t serial, struct wl_surface* surface,
|
|
struct wl_array* keys )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
INVOKE_WCB( *win, Entry, ( GLUT_ENTERED ) );
|
|
}
|
|
static void fghKeyboardLeave( void* data, struct wl_keyboard* keyboard,
|
|
uint32_t serial, struct wl_surface* surface )
|
|
{
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
INVOKE_WCB( *win, Entry, ( GLUT_LEFT ) );
|
|
}
|
|
static void fghKeyboardKey( void* data, struct wl_keyboard* keyboard,
|
|
uint32_t serial, uint32_t time,
|
|
uint32_t key, uint32_t state )
|
|
{
|
|
SFG_PlatformDisplay* pDisplay = data;
|
|
SFG_Window* win = fgStructure.CurrentWindow;
|
|
const xkb_keysym_t* syms;
|
|
|
|
xkb_state_key_get_syms( pDisplay->xkb_state,
|
|
key + 8, &syms );
|
|
fghKeyboardInterpretKeysym( win, key, syms[0], state );
|
|
}
|
|
static void fghKeyboardModifiers( void* data, struct wl_keyboard* keyboard,
|
|
uint32_t serial, uint32_t mods_depr,
|
|
uint32_t mods_latch, uint32_t mods_lock,
|
|
uint32_t group )
|
|
{
|
|
}
|
|
static const struct wl_keyboard_listener fghKeyboardListener =
|
|
{
|
|
fghKeyboardKeymap,
|
|
fghKeyboardEnter,
|
|
fghKeyboardLeave,
|
|
fghKeyboardKey,
|
|
fghKeyboardModifiers
|
|
};
|
|
|
|
|
|
/*
|
|
* Discover potential input device(s) (keyboard, pointer, touch)
|
|
*/
|
|
static void fghSeatCapabilities( void* data,
|
|
struct wl_seat* seat,
|
|
enum wl_seat_capability capabilities )
|
|
{
|
|
SFG_PlatformDisplay* pDisplay = data;
|
|
|
|
if( capabilities & WL_SEAT_CAPABILITY_KEYBOARD )
|
|
{
|
|
pDisplay->xkb_context = xkb_context_new ( 0 );
|
|
pDisplay->keyboard = wl_seat_get_keyboard( seat );
|
|
wl_keyboard_add_listener( pDisplay->keyboard,
|
|
&fghKeyboardListener,
|
|
pDisplay );
|
|
}
|
|
|
|
if( capabilities & WL_SEAT_CAPABILITY_POINTER )
|
|
{
|
|
pDisplay->pointer = wl_seat_get_pointer( seat );
|
|
wl_pointer_add_listener( pDisplay->pointer,
|
|
&fghPointerListener,
|
|
pDisplay );
|
|
}
|
|
|
|
if( capabilities & WL_SEAT_CAPABILITY_TOUCH )
|
|
{
|
|
pDisplay->touch = wl_seat_get_touch( seat );
|
|
wl_touch_add_listener( pDisplay->touch,
|
|
&fghTouchListener,
|
|
pDisplay );
|
|
}
|
|
}
|
|
static const struct wl_seat_listener fghSeatListener =
|
|
{
|
|
fghSeatCapabilities
|
|
};
|
|
|
|
|
|
/*
|
|
* Try initializing the input device(s)
|
|
*/
|
|
void fgPlatformInitialiseInputDevices( void )
|
|
{
|
|
wl_seat_add_listener( fgDisplay.pDisplay.seat,
|
|
&fghSeatListener,
|
|
&fgDisplay.pDisplay );
|
|
|
|
wl_display_roundtrip( fgDisplay.pDisplay.display );
|
|
}
|
|
|
|
/*
|
|
* Try closing the input device(s)
|
|
*/
|
|
void fgPlatformCloseInputDevices( void )
|
|
{
|
|
if( fgDisplay.pDisplay.touch )
|
|
wl_touch_destroy( fgDisplay.pDisplay.touch );
|
|
if( fgDisplay.pDisplay.pointer )
|
|
wl_pointer_destroy( fgDisplay.pDisplay.pointer );
|
|
if( fgDisplay.pDisplay.keyboard )
|
|
wl_keyboard_destroy( fgDisplay.pDisplay.keyboard );
|
|
if( fgDisplay.pDisplay.xkb_state )
|
|
xkb_state_unref( fgDisplay.pDisplay.xkb_state );
|
|
if( fgDisplay.pDisplay.xkb_context )
|
|
xkb_context_unref( fgDisplay.pDisplay.xkb_context );
|
|
}
|
|
|
|
|
|
/*
|
|
* Wayland backend will not be implementing spaceball at all
|
|
*/
|
|
void fgPlatformInitializeSpaceball( void )
|
|
{
|
|
}
|
|
void fgPlatformSpaceballClose( void )
|
|
{
|
|
}
|
|
void fgPlatformSpaceballSetWindow( SFG_Window *window )
|
|
{
|
|
}
|
|
int fgPlatformHasSpaceball( void )
|
|
{
|
|
return 0;
|
|
}
|
|
int fgPlatformSpaceballNumButtons( void )
|
|
{
|
|
return 0;
|
|
}
|
|
|