efl/src/lib/ecore_x/xcb/ecore_xcb.c

1590 lines
41 KiB
C

#include "ecore_xcb_private.h"
#include <X11/Xlib-xcb.h>
#include <dlfcn.h>
/* local function prototypes */
static int _ecore_xcb_shutdown(Eina_Bool close_display);
static Eina_Bool _ecore_xcb_fd_handle(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED);
static Eina_Bool _ecore_xcb_fd_handle_buff(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED);
static Eina_Bool _ecore_xcb_idle_enter(void *data EINA_UNUSED);
/* local variables */
static int _ecore_xcb_init_count = 0;
static int _ecore_xcb_grab_count = 0;
static Ecore_Fd_Handler *_ecore_xcb_fd_handler = NULL;
static xcb_generic_event_t *_ecore_xcb_event_buffered = NULL;
static Ecore_Idle_Enterer *_ecore_xcb_idle_enterer = NULL;
static Ecore_X_Version _version = { VMAJ, VMIN, VMIC, VREV };
EAPI Ecore_X_Version *ecore_x_version = &_version;
/* external variables */
int _ecore_xcb_log_dom = -1;
Ecore_X_Display *_ecore_xcb_display = NULL;
Ecore_X_Connection *_ecore_xcb_conn = NULL;
Ecore_X_Screen *_ecore_xcb_screen = NULL;
Ecore_X_Atom _ecore_xcb_atoms_wm_protocol[ECORE_X_WM_PROTOCOL_NUM];
double _ecore_xcb_double_click_time = 0.25;
/**
* @defgroup Ecore_X_Init_Group X Library Init and Shutdown Functions
* @ingroup Ecore_X_Group
*
* Functions that start and shut down the Ecore X Library.
*/
/**
* Initialize the X display connection to the given display.
*
* @param name Display target name. If @c NULL, the default display is
* assumed.
* @return The number of times the library has been initialized without
* being shut down. 0 is returned if an error occurs.
* @ingroup Ecore_X_Init_Group
*/
EAPI int
ecore_x_init(const char *name)
{
char *gl = NULL;
uint32_t mask, list[1];
/* check if we have initialized already */
if (++_ecore_xcb_init_count != 1)
return _ecore_xcb_init_count;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* try to initialize eina */
if (!eina_init()) return --_ecore_xcb_init_count;
/* setup ecore_xcb log domain */
_ecore_xcb_log_dom =
eina_log_domain_register("ecore_x", ECORE_XCB_DEFAULT_LOG_COLOR);
if (_ecore_xcb_log_dom < 0)
{
EINA_LOG_ERR("Cannot create Ecore Xcb log domain");
eina_shutdown();
return --_ecore_xcb_init_count;
}
/* try to initialize ecore */
if (!ecore_init())
{
/* unregister log domain */
eina_log_domain_unregister(_ecore_xcb_log_dom);
_ecore_xcb_log_dom = -1;
eina_shutdown();
return --_ecore_xcb_init_count;
}
/* try to initialize ecore_event */
if (!ecore_event_init())
{
/* unregister log domain */
eina_log_domain_unregister(_ecore_xcb_log_dom);
_ecore_xcb_log_dom = -1;
ecore_shutdown();
eina_shutdown();
return --_ecore_xcb_init_count;
}
/* NB: XLib has XInitThreads */
/* check for env var which says we are not going to use GL @ all
*
* NB: This is done because if someone wants a 'pure' xcb implementation
* of ecore_x, all they need do is export this variable in the environment
* and ecore_x will not use xlib stuff at all.
*
* The upside is you can get pure xcb-based ecore_x (w/ all the speed), but
* there is a down-side here in that you cannot get OpenGL without XLib :(
*/
if ((gl = getenv("ECORE_X_NO_XLIB")))
{
/* we found the env var that says 'Yes, we are not ever gonna try
* OpenGL so it is safe to not use XLib at all' */
/* try to connect to the display server */
_ecore_xcb_conn = xcb_connect(name, NULL);
}
else
{
/* env var was not specified, so we will assume that the user
* may want opengl @ some point. connect this way for opengl to work */
void *libxcb, *libxlib;
Display *(*_real_display)(const char *display);
xcb_connection_t *(*_real_connection)(Display * dpy);
void (*_real_queue)(Display *dpy, enum XEventQueueOwner owner);
int (*_real_close)(Display *dpy);
#ifdef EVAS_FRAME_QUEUING
Status (*_real_threads)(void);
#endif
/* want to dlopen here to avoid actual library linkage */
libxlib = dlopen("libX11.so", (RTLD_LAZY | RTLD_GLOBAL));
if (!libxlib)
libxlib = dlopen("libX11.so.6", (RTLD_LAZY | RTLD_GLOBAL));
if (!libxlib)
libxlib = dlopen("libX11.so.6.3.0", (RTLD_LAZY | RTLD_GLOBAL));
if (!libxlib)
{
ERR("Could not dlsym to libX11");
/* unregister log domain */
eina_log_domain_unregister(_ecore_xcb_log_dom);
_ecore_xcb_log_dom = -1;
ecore_event_shutdown();
ecore_shutdown();
eina_shutdown();
return --_ecore_xcb_init_count;
}
libxcb = dlopen("libX11-xcb.so", (RTLD_LAZY | RTLD_GLOBAL));
if (!libxcb)
libxcb = dlopen("libX11-xcb.so.1", (RTLD_LAZY | RTLD_GLOBAL));
if (!libxcb)
libxcb = dlopen("libX11-xcb.so.1.0.0", (RTLD_LAZY | RTLD_GLOBAL));
if (!libxcb)
{
ERR("Could not dlsym to libX11-xcb");
/* unregister log domain */
eina_log_domain_unregister(_ecore_xcb_log_dom);
_ecore_xcb_log_dom = -1;
ecore_event_shutdown();
ecore_shutdown();
eina_shutdown();
return --_ecore_xcb_init_count;
}
_real_display = dlsym(libxlib, "XOpenDisplay");
_real_close = dlsym(libxlib, "XCloseDisplay");
_real_connection = dlsym(libxcb, "XGetXCBConnection");
_real_queue = dlsym(libxcb, "XSetEventQueueOwner");
#ifdef EVAS_FRAME_QUEUING
_real_threads = dlsym(libxlib, "XInitThreads");
#endif
if (_real_display)
{
#ifdef EVAS_FRAME_QUEUING
if (_real_threads) _real_threads();
#endif
_ecore_xcb_display = _real_display(name);
if (!_ecore_xcb_display)
{
ERR("Could not open Display via XLib");
/* unregister log domain */
eina_log_domain_unregister(_ecore_xcb_log_dom);
_ecore_xcb_log_dom = -1;
ecore_event_shutdown();
ecore_shutdown();
eina_shutdown();
return --_ecore_xcb_init_count;
}
if (_real_connection)
_ecore_xcb_conn = _real_connection(_ecore_xcb_display);
if (!_ecore_xcb_conn)
{
ERR("Could not get XCB Connection from XLib");
if (_real_close) _real_close(_ecore_xcb_display);
/* unregister log domain */
eina_log_domain_unregister(_ecore_xcb_log_dom);
_ecore_xcb_log_dom = -1;
ecore_event_shutdown();
ecore_shutdown();
eina_shutdown();
return --_ecore_xcb_init_count;
}
if (_real_queue)
_real_queue(_ecore_xcb_display, XCBOwnsEventQueue);
}
}
if (xcb_connection_has_error(_ecore_xcb_conn))
{
CRIT("XCB Connection has error");
eina_log_domain_unregister(_ecore_xcb_log_dom);
_ecore_xcb_log_dom = -1;
ecore_event_shutdown();
ecore_shutdown();
eina_shutdown();
return --_ecore_xcb_init_count;
}
/* grab the default screen */
_ecore_xcb_screen =
xcb_setup_roots_iterator(xcb_get_setup(_ecore_xcb_conn)).data;
/* NB: This method of init/finalize extensions first, then atoms
* Does end up being 2 round trips to X, BUT if we do extensions init then
* atoms init first, and call the 'finalize' functions later, we end up
* being slower, so it's a trade-off. This current method clocks in
* around 0.003 for fetching atoms VS 0.010 for init both then finalize */
/* prefetch extension data */
_ecore_xcb_extensions_init();
/* finalize extensions */
_ecore_xcb_extensions_finalize();
/* set keyboard autorepeat */
mask = XCB_KB_AUTO_REPEAT_MODE;
list[0] = XCB_AUTO_REPEAT_MODE_ON;
xcb_change_keyboard_control(_ecore_xcb_conn, mask, list);
/* setup xcb events */
_ecore_xcb_events_init();
/* setup xcb keymasks */
_ecore_xcb_keymap_init();
/* finalize xcb keymasks */
_ecore_xcb_keymap_finalize();
/* setup ecore fd handler */
_ecore_xcb_fd_handler =
ecore_main_fd_handler_add(xcb_get_file_descriptor(_ecore_xcb_conn),
ECORE_FD_READ, _ecore_xcb_fd_handle,
_ecore_xcb_conn, _ecore_xcb_fd_handle_buff,
_ecore_xcb_conn);
if (!_ecore_xcb_fd_handler)
return _ecore_xcb_shutdown(EINA_TRUE);
/* prefetch atoms */
_ecore_xcb_atoms_init();
/* finalize atoms */
_ecore_xcb_atoms_finalize();
/* icccm_init: dummy function */
ecore_x_icccm_init();
/* setup netwm */
ecore_x_netwm_init();
/* old e hints init: dummy function */
ecore_x_e_init();
_ecore_xcb_atoms_wm_protocol[ECORE_X_WM_PROTOCOL_DELETE_REQUEST] =
ECORE_X_ATOM_WM_DELETE_WINDOW;
_ecore_xcb_atoms_wm_protocol[ECORE_X_WM_PROTOCOL_TAKE_FOCUS] =
ECORE_X_ATOM_WM_TAKE_FOCUS;
_ecore_xcb_atoms_wm_protocol[ECORE_X_NET_WM_PROTOCOL_PING] =
ECORE_X_ATOM_NET_WM_PING;
_ecore_xcb_atoms_wm_protocol[ECORE_X_NET_WM_PROTOCOL_SYNC_REQUEST] =
ECORE_X_ATOM_NET_WM_SYNC_REQUEST;
/* setup selection */
_ecore_xcb_selection_init();
/* setup dnd */
_ecore_xcb_dnd_init();
_ecore_xcb_idle_enterer =
ecore_idle_enterer_add(_ecore_xcb_idle_enter, NULL);
return _ecore_xcb_init_count;
}
/**
* Shuts down the Ecore X library.
*
* In shutting down the library, the X display connection is terminated
* and any event handlers for it are removed.
*
* @return The number of times the library has been initialized without
* being shut down.
* @ingroup Ecore_X_Init_Group
*/
EAPI int
ecore_x_shutdown(void)
{
return _ecore_xcb_shutdown(EINA_TRUE);
}
/**
* Shuts down the Ecore X library.
*
* As ecore_x_shutdown, except do not close Display, only connection.
*
* @return The number of times the library has been initialized without
* being shut down. 0 is returned if an error occurs.
* @ingroup Ecore_X_Init_Group
*/
EAPI int
ecore_x_disconnect(void)
{
return _ecore_xcb_shutdown(EINA_FALSE);
}
/**
* @defgroup Ecore_X_Flush_Group X Synchronization Functions
* @ingroup Ecore_X_Group
*
* Functions that ensure that all commands that have been issued by the
* Ecore X library have been sent to the server.
*/
/**
* Sends all X commands in the X Display buffer.
* @ingroup Ecore_X_Flush_Group
*/
EAPI void
ecore_x_flush(void)
{
// LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
xcb_flush(_ecore_xcb_conn);
}
/**
* Retrieves the Ecore_X_Screen handle used for the current X connection.
* @return The current default screen.
* @ingroup Ecore_X_Display_Attr_Group
*/
EAPI Ecore_X_Screen *
ecore_x_default_screen_get(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
return (Ecore_X_Screen *)_ecore_xcb_screen;
}
EAPI Ecore_X_Connection *
ecore_x_connection_get(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
return (Ecore_X_Connection *)_ecore_xcb_conn;
}
/**
* Return the last event time
*/
EAPI Ecore_X_Time
ecore_x_current_time_get(void)
{
return _ecore_xcb_events_last_time_get();
}
/**
* Flushes the command buffer and waits until all requests have been
* processed by the server.
* @ingroup Ecore_X_Flush_Group
*/
EAPI void
ecore_x_sync(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
free(xcb_get_input_focus_reply(_ecore_xcb_conn,
xcb_get_input_focus_unchecked(_ecore_xcb_conn),
NULL));
}
EAPI void
ecore_x_grab(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
_ecore_xcb_grab_count++;
if (_ecore_xcb_grab_count == 1)
xcb_grab_server(_ecore_xcb_conn);
}
EAPI void
ecore_x_ungrab(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
_ecore_xcb_grab_count--;
if (_ecore_xcb_grab_count < 0) _ecore_xcb_grab_count = 0;
if (_ecore_xcb_grab_count == 0)
xcb_ungrab_server(_ecore_xcb_conn);
}
/**
* Send client message with given type and format 32.
*
* @param win The window the message is sent to.
* @param type The client message type.
* @param mask The mask of the message to be sent.
* @param d0 The client message data item 1
* @param d1 The client message data item 2
* @param d2 The client message data item 3
* @param d3 The client message data item 4
* @param d4 The client message data item 5
*
* @return @c EINA_TRUE on success @c EINA_FALSE otherwise.
*/
EAPI Eina_Bool
ecore_x_client_message32_send(Ecore_X_Window win, Ecore_X_Atom type,
Ecore_X_Event_Mask mask,
long d0, long d1, long d2, long d3, long d4)
{
xcb_client_message_event_t ev;
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
memset(&ev, 0, sizeof(xcb_client_message_event_t));
ev.response_type = XCB_CLIENT_MESSAGE;
ev.format = 32;
ev.window = win;
ev.type = type;
ev.data.data32[0] = (uint32_t)d0;
ev.data.data32[1] = (uint32_t)d1;
ev.data.data32[2] = (uint32_t)d2;
ev.data.data32[3] = (uint32_t)d3;
ev.data.data32[4] = (uint32_t)d4;
cookie = xcb_send_event(_ecore_xcb_conn, 0, win, mask, (const char *)&ev);
err = xcb_request_check(_ecore_xcb_conn, cookie);
if (err)
{
DBG("Problem Sending Event");
DBG("\tType: %d", type);
DBG("\tWin: %d", win);
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
/**
* Send client message with given type and format 8.
*
* @param win The window the message is sent to.
* @param type The client message type.
* @param data Data to be sent.
* @param len Number of data bytes, max @c 20.
*
* @return @c EINA_TRUE on success @c EINA_FALSE otherwise.
*/
EAPI Eina_Bool
ecore_x_client_message8_send(Ecore_X_Window win, Ecore_X_Atom type,
const void *data, int len)
{
xcb_client_message_event_t ev;
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
memset(&ev, 0, sizeof(xcb_client_message_event_t));
ev.response_type = XCB_CLIENT_MESSAGE;
ev.format = 8;
ev.window = win;
ev.type = type;
if (len > 20) len = 20;
memcpy(ev.data.data8, data, len);
memset(ev.data.data8 + len, 0, 20 - len);
cookie = xcb_send_event(_ecore_xcb_conn, 0, win,
XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
err = xcb_request_check(_ecore_xcb_conn, cookie);
if (err)
{
DBG("Problem Sending Event");
DBG("\tType: %d", type);
DBG("\tWin: %d", win);
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_x_mouse_down_send(Ecore_X_Window win, int x, int y, int b)
{
xcb_translate_coordinates_cookie_t cookie;
xcb_translate_coordinates_reply_t *reply;
xcb_button_press_event_t ev;
xcb_void_cookie_t vcookie;
xcb_generic_error_t *err;
Ecore_X_Window root = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
root = ecore_x_window_root_get(win);
cookie = xcb_translate_coordinates(_ecore_xcb_conn, win, root, x, y);
reply = xcb_translate_coordinates_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
memset(&ev, 0, sizeof(xcb_button_press_event_t));
ev.response_type = XCB_BUTTON_PRESS;
ev.event = win;
ev.child = win;
ev.root = root;
ev.event_x = x;
ev.event_y = y;
ev.same_screen = 1;
ev.state = 1 << b;
ev.detail = b; // xcb uses detail for button
ev.root_x = reply->dst_x;
ev.root_y = reply->dst_y;
ev.time = ecore_x_current_time_get();
free(reply);
vcookie = xcb_send_event(_ecore_xcb_conn, 1, win,
XCB_EVENT_MASK_BUTTON_PRESS, (const char *)&ev);
err = xcb_request_check(_ecore_xcb_conn, vcookie);
if (err)
{
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_x_mouse_up_send(Ecore_X_Window win, int x, int y, int b)
{
xcb_translate_coordinates_cookie_t cookie;
xcb_translate_coordinates_reply_t *reply;
xcb_button_release_event_t ev;
xcb_void_cookie_t vcookie;
xcb_generic_error_t *err;
Ecore_X_Window root = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
root = ecore_x_window_root_get(win);
cookie = xcb_translate_coordinates(_ecore_xcb_conn, win, root, x, y);
reply = xcb_translate_coordinates_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
memset(&ev, 0, sizeof(xcb_button_release_event_t));
ev.response_type = XCB_BUTTON_RELEASE;
ev.event = win;
ev.child = win;
ev.root = root;
ev.event_x = x;
ev.event_y = y;
ev.same_screen = 1;
ev.state = 0;
ev.root_x = reply->dst_x;
ev.root_y = reply->dst_y;
ev.detail = b; // xcb uses detail for button
ev.time = ecore_x_current_time_get();
free(reply);
vcookie = xcb_send_event(_ecore_xcb_conn, 1, win,
XCB_EVENT_MASK_BUTTON_RELEASE, (const char *)&ev);
err = xcb_request_check(_ecore_xcb_conn, vcookie);
if (err)
{
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_x_mouse_move_send(Ecore_X_Window win, int x, int y)
{
xcb_translate_coordinates_cookie_t cookie;
xcb_translate_coordinates_reply_t *reply;
xcb_motion_notify_event_t ev;
xcb_void_cookie_t vcookie;
xcb_generic_error_t *err;
Ecore_X_Window root = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
root = ecore_x_window_root_get(win);
cookie = xcb_translate_coordinates(_ecore_xcb_conn, win, root, x, y);
reply = xcb_translate_coordinates_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
memset(&ev, 0, sizeof(xcb_motion_notify_event_t));
ev.response_type = XCB_MOTION_NOTIFY;
ev.event = win;
ev.child = win;
ev.root = root;
ev.event_x = x;
ev.event_y = y;
ev.same_screen = 1;
ev.state = 0;
ev.detail = 0; // xcb uses 'detail' for is_hint
ev.root_x = reply->dst_x;
ev.root_y = reply->dst_y;
ev.time = ecore_x_current_time_get();
free(reply);
vcookie = xcb_send_event(_ecore_xcb_conn, 1, win,
XCB_EVENT_MASK_POINTER_MOTION, (const char *)&ev);
err = xcb_request_check(_ecore_xcb_conn, vcookie);
if (err)
{
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_x_mouse_in_send(Ecore_X_Window win, int x, int y)
{
xcb_translate_coordinates_cookie_t cookie;
xcb_translate_coordinates_reply_t *reply;
xcb_enter_notify_event_t ev;
xcb_void_cookie_t vcookie;
xcb_generic_error_t *err;
Ecore_X_Window root = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
root = ecore_x_window_root_get(win);
cookie = xcb_translate_coordinates(_ecore_xcb_conn, win, root, x, y);
reply = xcb_translate_coordinates_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
memset(&ev, 0, sizeof(xcb_enter_notify_event_t));
ev.response_type = XCB_ENTER_NOTIFY;
ev.event = win;
ev.child = win;
ev.root = root;
ev.event_x = x;
ev.event_y = y;
ev.same_screen_focus = 1;
ev.mode = XCB_NOTIFY_MODE_NORMAL;
ev.detail = XCB_NOTIFY_DETAIL_NONLINEAR;
/* ev.focus = 0; */
ev.state = 0;
ev.root_x = reply->dst_x;
ev.root_y = reply->dst_y;
ev.time = ecore_x_current_time_get();
free(reply);
vcookie = xcb_send_event(_ecore_xcb_conn, 1, win,
XCB_EVENT_MASK_ENTER_WINDOW, (const char *)&ev);
err = xcb_request_check(_ecore_xcb_conn, vcookie);
if (err)
{
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_x_mouse_out_send(Ecore_X_Window win, int x, int y)
{
xcb_translate_coordinates_cookie_t cookie;
xcb_translate_coordinates_reply_t *reply;
xcb_leave_notify_event_t ev;
xcb_void_cookie_t vcookie;
xcb_generic_error_t *err;
Ecore_X_Window root = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
root = ecore_x_window_root_get(win);
cookie = xcb_translate_coordinates(_ecore_xcb_conn, win, root, x, y);
reply = xcb_translate_coordinates_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
memset(&ev, 0, sizeof(xcb_leave_notify_event_t));
ev.response_type = XCB_LEAVE_NOTIFY;
ev.event = win;
ev.child = win;
ev.root = root;
ev.event_x = x;
ev.event_y = y;
ev.same_screen_focus = 1;
ev.mode = XCB_NOTIFY_MODE_NORMAL;
ev.detail = XCB_NOTIFY_DETAIL_NONLINEAR;
/* ev.focus = 0; */
ev.state = 0;
ev.root_x = reply->dst_x;
ev.root_y = reply->dst_y;
ev.time = ecore_x_current_time_get();
free(reply);
vcookie = xcb_send_event(_ecore_xcb_conn, 1, win,
XCB_EVENT_MASK_LEAVE_WINDOW, (const char *)&ev);
err = xcb_request_check(_ecore_xcb_conn, vcookie);
if (err)
{
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_x_keyboard_grab(Ecore_X_Window win)
{
xcb_grab_keyboard_cookie_t cookie;
xcb_grab_keyboard_reply_t *reply;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
cookie =
xcb_grab_keyboard_unchecked(_ecore_xcb_conn, 0, win, XCB_CURRENT_TIME,
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
reply = xcb_grab_keyboard_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
free(reply);
return EINA_TRUE;
}
EAPI void
ecore_x_keyboard_ungrab(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
xcb_ungrab_keyboard(_ecore_xcb_conn, XCB_CURRENT_TIME);
}
EAPI void
ecore_x_pointer_xy_get(Ecore_X_Window win, int *x, int *y)
{
xcb_query_pointer_cookie_t cookie;
xcb_query_pointer_reply_t *reply;
// LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
// if (!win) win = ((xcb_screen_t *)_ecore_xcb_screen)->root;
if (x) *x = -1;
if (y) *y = -1;
cookie = xcb_query_pointer_unchecked(_ecore_xcb_conn, win);
reply = xcb_query_pointer_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return;
if (x) *x = reply->win_x;
if (y) *y = reply->win_y;
free(reply);
}
EAPI Eina_Bool
ecore_x_pointer_control_set(int accel_num, int accel_denom, int threshold)
{
xcb_void_cookie_t vcookie;
xcb_generic_error_t *err;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
vcookie =
xcb_change_pointer_control_checked(_ecore_xcb_conn,
accel_num, accel_denom, threshold,
1, 1);
err = xcb_request_check(_ecore_xcb_conn, vcookie);
if (err)
{
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_x_pointer_control_get(int *accel_num, int *accel_denom, int *threshold)
{
xcb_get_pointer_control_cookie_t cookie;
xcb_get_pointer_control_reply_t *reply;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
if (accel_num) *accel_num = 0;
if (accel_denom) *accel_denom = 0;
if (threshold) *threshold = 0;
cookie = xcb_get_pointer_control_unchecked(_ecore_xcb_conn);
reply = xcb_get_pointer_control_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
if (accel_num) *accel_num = reply->acceleration_numerator;
if (accel_denom) *accel_denom = reply->acceleration_denominator;
if (threshold) *threshold = reply->threshold;
free(reply);
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_x_pointer_mapping_set(unsigned char *map, int nmap)
{
xcb_set_pointer_mapping_cookie_t cookie;
xcb_set_pointer_mapping_reply_t *reply;
Eina_Bool ret = EINA_FALSE;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
cookie = xcb_set_pointer_mapping_unchecked(_ecore_xcb_conn, nmap, map);
reply = xcb_set_pointer_mapping_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
if (reply->status == XCB_MAPPING_STATUS_SUCCESS)
ret = EINA_TRUE;
free(reply);
return ret;
}
EAPI Eina_Bool
ecore_x_pointer_mapping_get(unsigned char *map, int nmap)
{
xcb_get_pointer_mapping_cookie_t cookie;
xcb_get_pointer_mapping_reply_t *reply;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
if (map) *map = 0;
nmap = 0;
cookie = xcb_get_pointer_mapping_unchecked(_ecore_xcb_conn);
reply = xcb_get_pointer_mapping_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
nmap = xcb_get_pointer_mapping_map_length(reply);
if (nmap <= 0)
{
free(reply);
return EINA_FALSE;
}
if (map)
{
uint8_t *tmp;
int i = 0;
tmp = xcb_get_pointer_mapping_map(reply);
for (i = 0; i < nmap; i++)
map[i] = tmp[i];
}
free(reply);
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_x_pointer_grab(Ecore_X_Window win)
{
xcb_grab_pointer_cookie_t cookie;
xcb_grab_pointer_reply_t *reply;
uint16_t mask;
Eina_Bool ret = EINA_FALSE;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
mask = (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
XCB_EVENT_MASK_POINTER_MOTION);
cookie = xcb_grab_pointer_unchecked(_ecore_xcb_conn, 0, win, mask,
XCB_GRAB_MODE_ASYNC,
XCB_GRAB_MODE_ASYNC,
XCB_NONE, XCB_NONE, XCB_CURRENT_TIME);
reply = xcb_grab_pointer_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
ret = (reply->status == XCB_GRAB_STATUS_SUCCESS) ? EINA_TRUE : EINA_FALSE;
free(reply);
return ret;
}
EAPI Eina_Bool
ecore_x_pointer_confine_grab(Ecore_X_Window win)
{
xcb_grab_pointer_cookie_t cookie;
xcb_grab_pointer_reply_t *reply;
uint16_t mask;
Eina_Bool ret = EINA_FALSE;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
mask = (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
XCB_EVENT_MASK_POINTER_MOTION);
cookie = xcb_grab_pointer_unchecked(_ecore_xcb_conn, 0, win, mask,
XCB_GRAB_MODE_ASYNC,
XCB_GRAB_MODE_ASYNC,
win, XCB_NONE, XCB_CURRENT_TIME);
reply = xcb_grab_pointer_reply(_ecore_xcb_conn, cookie, NULL);
if (!reply) return EINA_FALSE;
ret = (reply->status == XCB_GRAB_STATUS_SUCCESS) ? EINA_TRUE : EINA_FALSE;
free(reply);
return ret;
}
EAPI void
ecore_x_pointer_ungrab(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
xcb_ungrab_pointer(_ecore_xcb_conn, XCB_CURRENT_TIME);
}
EAPI Eina_Bool
ecore_x_pointer_warp(Ecore_X_Window win, int x, int y)
{
xcb_void_cookie_t vcookie;
xcb_generic_error_t *err;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
vcookie =
xcb_warp_pointer_checked(_ecore_xcb_conn, XCB_NONE, win, 0, 0, 0, 0, x, y);
err = xcb_request_check(_ecore_xcb_conn, vcookie);
if (err)
{
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
/**
* Invoke the standard system beep to alert users
*
* @param percent The volume at which the bell rings. Must be in the range
* [-100,+100]. If percent >= 0, the final volume will be:
* base - [(base * percent) / 100] + percent
* Otherwise, it's calculated as:
* base + [(base * percent) / 100]
* where @c base is the bell's base volume as set by XChangeKeyboardControl(3).
*
* @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
*/
EAPI Eina_Bool
ecore_x_bell(int percent)
{
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
CHECK_XCB_CONN;
// FIXME: Use unchecked version after development is ironed out
cookie = xcb_bell_checked(_ecore_xcb_conn, percent);
err = xcb_request_check(_ecore_xcb_conn, cookie);
if (err)
{
_ecore_xcb_error_handle(err);
free(err);
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI void
ecore_x_display_size_get(Ecore_X_Display *dsp EINA_UNUSED, int *w, int *h)
{
xcb_screen_t *screen;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
/* grab the default screen */
screen = xcb_setup_roots_iterator(xcb_get_setup(_ecore_xcb_conn)).data;
if (w) *w = screen->width_in_pixels;
if (h) *h = screen->height_in_pixels;
}
EAPI unsigned long
ecore_x_display_black_pixel_get(Ecore_X_Display *dsp EINA_UNUSED)
{
xcb_screen_t *screen;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
/* grab the default screen */
screen = xcb_setup_roots_iterator(xcb_get_setup(_ecore_xcb_conn)).data;
return screen->black_pixel;
}
EAPI unsigned long
ecore_x_display_white_pixel_get(Ecore_X_Display *dsp EINA_UNUSED)
{
xcb_screen_t *screen;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
/* grab the default screen */
screen = xcb_setup_roots_iterator(xcb_get_setup(_ecore_xcb_conn)).data;
return screen->white_pixel;
}
EAPI void
ecore_x_pointer_last_xy_get(int *x, int *y)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (x) *x = _ecore_xcb_event_last_root_x;
if (y) *y = _ecore_xcb_event_last_root_y;
}
EAPI void
ecore_x_focus_reset(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
xcb_set_input_focus(_ecore_xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT,
((xcb_screen_t *)_ecore_xcb_screen)->root,
XCB_CURRENT_TIME);
// ecore_x_flush();
}
EAPI void
ecore_x_events_allow_all(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
xcb_allow_events(_ecore_xcb_conn, XCB_ALLOW_ASYNC_BOTH, XCB_CURRENT_TIME);
// ecore_x_flush();
}
/**
* Kill a specific client
*
* You can kill a specific client owning window @p win
*
* @param win Window of the client to be killed
*/
EAPI void
ecore_x_kill(Ecore_X_Window win)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
xcb_kill_client(_ecore_xcb_conn, win);
// ecore_x_flush();
}
/**
* Kill all clients with subwindows under a given window.
*
* You can kill all clients connected to the X server by using
* @ref ecore_x_window_root_list to get a list of root windows, and
* then passing each root window to this function.
*
* @param root The window whose children will be killed.
*/
EAPI void
ecore_x_killall(Ecore_X_Window root)
{
int screens = 0, i = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
ecore_x_grab();
screens = xcb_setup_roots_iterator(xcb_get_setup(_ecore_xcb_conn)).rem;
/* Traverse window tree starting from root, and drag each
* before the firing squad */
for (i = 0; i < screens; ++i)
{
xcb_query_tree_cookie_t cookie;
xcb_query_tree_reply_t *reply;
cookie = xcb_query_tree_unchecked(_ecore_xcb_conn, root);
reply = xcb_query_tree_reply(_ecore_xcb_conn, cookie, NULL);
if (reply)
{
xcb_window_t *wins = NULL;
int tree_c_len, j = 0;
wins = xcb_query_tree_children(reply);
tree_c_len = xcb_query_tree_children_length(reply);
for (j = 0; j < tree_c_len; j++)
xcb_kill_client(_ecore_xcb_conn, wins[j]);
free(reply);
}
}
ecore_x_ungrab();
ecore_x_sync(); // needed
}
/**
* Return the screen DPI
*
* This is a simplistic call to get DPI. It does not account for differing
* DPI in the x amd y axes nor does it account for multihead or xinerama and
* xrander where different parts of the screen may have differen DPI etc.
*
* @return the general screen DPI (dots/pixels per inch).
*/
EAPI int
ecore_x_dpi_get(void)
{
uint16_t mw = 0, w = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
mw = ((xcb_screen_t *)_ecore_xcb_screen)->width_in_millimeters;
if (mw <= 0) return 75;
w = ((xcb_screen_t *)_ecore_xcb_screen)->width_in_pixels;
return (((w * 254) / mw) + 5) / 10;
}
/**
* @defgroup Ecore_X_Display_Attr_Group X Display Attributes
* @ingroup Ecore_X_Group
*
* Functions that set and retrieve X display attributes.
*/
/**
* Retrieves the Ecore_X_Display handle used for the current X connection.
* @return The current X display.
* @ingroup Ecore_X_Display_Attr_Group
*/
EAPI Ecore_X_Display *
ecore_x_display_get(void)
{
char *gl = NULL;
CHECK_XCB_CONN;
/* if we have the 'dont use xlib' env var, then we are not using
* XLib and thus cannot return a real XDisplay.
*
* NB: This may break EFL in some places and needs lots of testing !!! */
if ((gl = getenv("ECORE_X_NO_XLIB")))
return (Ecore_X_Display *)_ecore_xcb_conn;
else /* we can safely return an XDisplay var */
return (Ecore_X_Display *)_ecore_xcb_display;
}
/**
* Retrieves the X display file descriptor.
* @return The current X display file descriptor.
* @ingroup Ecore_X_Display_Attr_Group
*/
EAPI int
ecore_x_fd_get(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
return xcb_get_file_descriptor(_ecore_xcb_conn);
}
EAPI void
ecore_x_passive_grab_replay_func_set(Eina_Bool (*func)(void *data, int type, void *event),
void *data)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
_ecore_xcb_window_grab_replay_func = func;
_ecore_xcb_window_grab_replay_data = data;
}
/**
* Retrieves the size of an Ecore_X_Screen.
* @param screen the handle to the screen to query.
* @param w where to return the width. May be NULL. Returns 0 on errors.
* @param h where to return the height. May be NULL. Returns 0 on errors.
* @ingroup Ecore_X_Display_Attr_Group
* @see ecore_x_default_screen_get()
*
* @since 1.1
*/
EAPI void
ecore_x_screen_size_get(const Ecore_X_Screen *screen, int *w, int *h)
{
xcb_screen_t *s;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (w) *w = 0;
if (h) *h = 0;
if (!(s = (xcb_screen_t *)screen)) return;
if (w) *w = s->width_in_pixels;
if (h) *h = s->height_in_pixels;
}
/**
* Retrieves the count of screens.
*
* @return The count of screens.
* @ingroup Ecore_X_Display_Attr_Group
*
* @since 1.1
*/
EAPI int
ecore_x_screen_count_get(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
return xcb_setup_roots_length(xcb_get_setup(_ecore_xcb_conn));
}
/**
* Retrieves the index number of the given screen.
*
* @param screen The screen for which index will be gotten.
* @return The index number of the screen.
* @ingroup Ecore_X_Display_Attr_Group
*
* @since 1.1
*/
EAPI int
ecore_x_screen_index_get(const Ecore_X_Screen *screen)
{
xcb_screen_iterator_t iter;
int i = 0;
CHECK_XCB_CONN;
iter =
xcb_setup_roots_iterator(xcb_get_setup(_ecore_xcb_conn));
for (; iter.rem; xcb_screen_next(&iter))
{
if (iter.data == (xcb_screen_t *)screen)
return i;
i++;
}
return 0;
}
/**
* Retrieves the screen based on index number.
*
* @param idx The index that will be used to retrieve the screen.
* @return The Ecore_X_Screen at this index.
* @ingroup Ecore_X_Display_Attr_Group
*
* @since 1.1
*/
EAPI Ecore_X_Screen *
ecore_x_screen_get(int idx)
{
xcb_screen_iterator_t iter;
int i = 0;
CHECK_XCB_CONN;
iter =
xcb_setup_roots_iterator(xcb_get_setup(_ecore_xcb_conn));
for (i = 0; iter.rem; xcb_screen_next(&iter), i++)
if (i == idx) return iter.data;
return NULL;
}
EAPI unsigned int
ecore_x_visual_id_get(Ecore_X_Visual visual)
{
return ((xcb_visualtype_t *)visual)->visual_id;
}
/**
* Retrieve the default Visual.
*
* @param disp The Display to get the Default Visual from
* @param screen The Screen.
*
* @return The default visual.
* @since 1.1.0
*/
EAPI Ecore_X_Visual
ecore_x_default_visual_get(Ecore_X_Display *disp EINA_UNUSED, Ecore_X_Screen *screen)
{
xcb_screen_t *s;
xcb_depth_iterator_t diter;
xcb_visualtype_iterator_t viter;
CHECK_XCB_CONN;
s = (xcb_screen_t *)screen;
diter = xcb_screen_allowed_depths_iterator(s);
for (; diter.rem; xcb_depth_next(&diter))
{
viter = xcb_depth_visuals_iterator(diter.data);
for (; viter.rem; xcb_visualtype_next(&viter))
{
if (viter.data->visual_id == s->root_visual)
return viter.data;
}
}
return 0;
}
/**
* Retrieve the default Colormap.
*
* @param disp The Display to get the Default Colormap from
* @param screen The Screen.
*
* @return The default colormap.
* @since 1.1.0
*/
EAPI Ecore_X_Colormap
ecore_x_default_colormap_get(Ecore_X_Display *disp EINA_UNUSED, Ecore_X_Screen *screen)
{
xcb_screen_t *s;
s = (xcb_screen_t *)screen;
return s->default_colormap;
}
/**
* Retrieve the default depth.
*
* @param disp The Display to get the Default Depth from
* @param screen The Screen.
*
* @return The default depth.
* @since 1.1.0
*/
EAPI int
ecore_x_default_depth_get(Ecore_X_Display *disp EINA_UNUSED, Ecore_X_Screen *screen)
{
xcb_screen_t *s;
s = (xcb_screen_t *)screen;
return s->root_depth;
}
EAPI void
ecore_x_xkb_select_group(int group)
{
// XXX: implement me */
}
/**
* Sets the timeout for a double and triple clicks to be flagged.
*
* This sets the time between clicks before the double_click flag is
* set in a button down event. If 3 clicks occur within double this
* time, the triple_click flag is also set.
*
* @param t The time in seconds
* @ingroup Ecore_X_Display_Attr_Group
*/
EAPI void
ecore_x_double_click_time_set(double t)
{
if (t < 0.0) t = 0.0;
_ecore_xcb_double_click_time = t;
}
/**
* Retrieves the double and triple click flag timeout.
*
* See @ref ecore_x_double_click_time_set for more information.
*
* @return The timeout for double clicks in seconds.
* @ingroup Ecore_X_Display_Attr_Group
*/
EAPI double
ecore_x_double_click_time_get(void)
{
return _ecore_xcb_double_click_time;
}
/* local function prototypes */
static int
_ecore_xcb_shutdown(Eina_Bool close_display)
{
if (--_ecore_xcb_init_count != 0)
return _ecore_xcb_init_count;
if (!_ecore_xcb_conn)
return _ecore_xcb_init_count;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
ecore_idle_enterer_del(_ecore_xcb_idle_enterer);
_ecore_xcb_idle_enterer = NULL;
if (_ecore_xcb_fd_handler)
ecore_main_fd_handler_del(_ecore_xcb_fd_handler);
/* disconnect from display server */
if (close_display)
xcb_disconnect(_ecore_xcb_conn);
else
{
close(xcb_get_file_descriptor(_ecore_xcb_conn));
_ecore_xcb_conn = NULL;
}
/* shutdown events */
_ecore_xcb_events_shutdown();
/* shutdown input extension */
_ecore_xcb_input_shutdown();
/* shutdown gesture extension */
_ecore_xcb_gesture_shutdown();
/* shutdown selection */
_ecore_xcb_selection_shutdown();
/* shutdown dnd */
_ecore_xcb_dnd_shutdown();
/* shutdown netwm */
ecore_x_netwm_shutdown();
/* shutdown keymap */
_ecore_xcb_keymap_shutdown();
/* shutdown ecore_event */
ecore_event_shutdown();
/* shutdown ecore */
ecore_shutdown();
/* unregister log domain */
eina_log_domain_unregister(_ecore_xcb_log_dom);
_ecore_xcb_log_dom = -1;
/* shutdown eina */
eina_shutdown();
return _ecore_xcb_init_count;
}
static Eina_Bool
_ecore_xcb_fd_handle(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
xcb_connection_t *conn;
xcb_generic_event_t *ev = NULL;
conn = (xcb_connection_t *)data;
if (_ecore_xcb_event_buffered)
{
_ecore_xcb_events_handle(_ecore_xcb_event_buffered);
free(_ecore_xcb_event_buffered);
_ecore_xcb_event_buffered = NULL;
}
// xcb_flush(conn);
while ((ev = xcb_poll_for_event(conn)))
{
/* NB: Ecore Xlib uses filterevent for xim, but xcb does not support
* xim, so no need for it here */
/* check for errors first */
if (xcb_connection_has_error(conn))
{
xcb_generic_error_t *err;
err = (xcb_generic_error_t *)ev;
_ecore_xcb_io_error_handle(err);
}
else
{
/* FIXME: Filter event for XIM */
_ecore_xcb_events_handle(ev);
free(ev);
}
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
_ecore_xcb_fd_handle_buff(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
xcb_connection_t *conn;
xcb_generic_event_t *ev = NULL;
conn = (xcb_connection_t *)data;
ev = xcb_poll_for_event(conn);
if (ev)
{
/* check for errors first */
if (xcb_connection_has_error(conn))
{
xcb_generic_error_t *err;
err = (xcb_generic_error_t *)ev;
_ecore_xcb_io_error_handle(err);
return ECORE_CALLBACK_CANCEL;
}
_ecore_xcb_event_buffered = ev;
return ECORE_CALLBACK_RENEW;
}
return ECORE_CALLBACK_CANCEL;
}
static Eina_Bool
_ecore_xcb_idle_enter(void *data EINA_UNUSED)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
CHECK_XCB_CONN;
xcb_flush(_ecore_xcb_conn);
return ECORE_CALLBACK_RENEW;
}