diff --git a/src/lib/ecore_drm/Ecore_Drm.h b/src/lib/ecore_drm/Ecore_Drm.h new file mode 100644 index 0000000000..e89d2df5b0 --- /dev/null +++ b/src/lib/ecore_drm/Ecore_Drm.h @@ -0,0 +1,171 @@ +#ifndef _ECORE_DRM_H +# define _ECORE_DRM_H + +# ifdef EAPI +# undef EAPI +# endif + +#ifdef _MSC_VER +# ifdef BUILDING_DLL +# define EAPI __declspec(dllexport) +# else // ifdef BUILDING_DLL +# define EAPI __declspec(dllimport) +# endif // ifdef BUILDING_DLL +#else // ifdef _MSC_VER +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else // if __GNUC__ >= 4 +# define EAPI +# endif // if __GNUC__ >= 4 +# else // ifdef __GNUC__ +# define EAPI +# endif // ifdef __GNUC__ +#endif // ifdef _MSC_VER + +typedef enum _Ecore_Drm_Op +{ + ECORE_DRM_OP_READ_FD_SET, + ECORE_DRM_OP_WRITE_FD_SET, + ECORE_DRM_OP_DEVICE_OPEN, + ECORE_DRM_OP_DEVICE_CLOSE, + ECORE_DRM_OP_DEVICE_MASTER_DROP, + ECORE_DRM_OP_DEVICE_MASTER_SET, + ECORE_DRM_OP_TTY_OPEN, + ECORE_DRM_OP_TTY_CLOSE +} Ecore_Drm_Op; + +typedef enum _Ecore_Drm_Op_Result +{ + ECORE_DRM_OP_SUCCESS, + ECORE_DRM_OP_FAILURE +} Ecore_Drm_Op_Result; + +typedef enum _Ecore_Drm_Evdev_Capabilities +{ + EVDEV_KEYBOARD = (1 << 0), + EVDEV_BUTTON = (1 << 1), + EVDEV_MOTION_ABS = (1 << 2), + EVDEV_MOTION_REL = (1 << 3), + EVDEV_TOUCH = (1 << 4), +} Ecore_Drm_Evdev_Capabilities; + +typedef enum _Ecore_Drm_Evdev_Event_Type +{ + EVDEV_NONE, + EVDEV_ABSOLUTE_TOUCH_DOWN, + EVDEV_ABSOLUTE_MOTION, + EVDEV_ABSOLUTE_TOUCH_UP, + EVDEV_ABSOLUTE_MT_DOWN, + EVDEV_ABSOLUTE_MT_MOTION, + EVDEV_ABSOLUTE_MT_UP, + EVDEV_RELATIVE_MOTION, +} Ecore_Drm_Evdev_Event_Type; + +typedef enum _Ecore_Drm_Seat_Capabilities +{ + EVDEV_SEAT_POINTER = (1 << 0), + EVDEV_SEAT_KEYBOARD = (1 << 1), + EVDEV_SEAT_TOUCH = (1 << 2), +} Ecore_Drm_Seat_Capabilities; + +/* structure for message passing */ +typedef struct _Ecore_Drm_Message +{ + int opcode, size; + void *data; +} Ecore_Drm_Message; + +/* structure for fb objects */ +typedef struct _Ecore_Drm_Fb +{ + Eina_Bool from_client : 1; + unsigned int id, hdl; + unsigned int stride, size; + int fd; + void *mmap; +/* #ifdef HAVE_GBM */ +/* struct gbm_bo *bo; */ +/* #endif */ +} Ecore_Drm_Fb; + +/* opaque structure to represent a drm device */ +typedef struct _Ecore_Drm_Device Ecore_Drm_Device; + +/* opaque structure to represent a drm output mode */ +typedef struct _Ecore_Drm_Output_Mode Ecore_Drm_Output_Mode; + +/* opaque structure to represent a drm output */ +typedef struct _Ecore_Drm_Output Ecore_Drm_Output; + +/* opaque structure to represent a drm udev input */ +typedef struct _Ecore_Drm_Input Ecore_Drm_Input; + +/* opaque structure to represent a drm evdev input */ +typedef struct _Ecore_Drm_Evdev Ecore_Drm_Evdev; + +/* opaque structure to represent a drm seat */ +typedef struct _Ecore_Drm_Seat Ecore_Drm_Seat; + +/* opaque structure to represent a drm sprite */ +typedef struct _Ecore_Drm_Sprite Ecore_Drm_Sprite; + +/** + * @file + * @brief Ecore functions for dealing with drm, virtual terminals + * + * @defgroup Ecore_Drm_Group Ecore_Drm - Drm Integration + * @ingroup Ecore + * + * Ecore_Drm provides a wrapper and functions for using libdrm + * + * @li @ref Ecore_Drm_Init_Group + * @li @ref Ecore_Drm_Device_Group + * @li @ref Ecore_Drm_Tty_Group + * @li @ref Ecore_Drm_Output_Group + * @li @ref Ecore_Drm_Input_Group + * @li @ref Ecore_Drm_Sprite_Group + * + */ + +EAPI int ecore_drm_init(void); +EAPI int ecore_drm_shutdown(void); + +/* EAPI void *ecore_drm_gbm_get(Ecore_Drm_Device *dev); */ +/* EAPI unsigned int ecore_drm_gbm_format_get(Ecore_Drm_Device *dev); */ + +EAPI Ecore_Drm_Device *ecore_drm_device_find(const char *name, const char *seat); +EAPI void ecore_drm_device_free(Ecore_Drm_Device *dev); +EAPI Eina_Bool ecore_drm_device_open(Ecore_Drm_Device *dev); +EAPI Eina_Bool ecore_drm_device_close(Ecore_Drm_Device *dev); +EAPI Eina_Bool ecore_drm_device_master_get(Ecore_Drm_Device *dev); +EAPI Eina_Bool ecore_drm_device_master_set(Ecore_Drm_Device *dev); +EAPI Eina_Bool ecore_drm_device_master_drop(Ecore_Drm_Device *dev); +EAPI int ecore_drm_device_fd_get(Ecore_Drm_Device *dev); + +EAPI Eina_Bool ecore_drm_tty_open(Ecore_Drm_Device *dev, const char *name); +EAPI Eina_Bool ecore_drm_tty_close(Ecore_Drm_Device *dev); +EAPI Eina_Bool ecore_drm_tty_release(Ecore_Drm_Device *dev); +EAPI Eina_Bool ecore_drm_tty_acquire(Ecore_Drm_Device *dev); + +EAPI Eina_Bool ecore_drm_outputs_create(Ecore_Drm_Device *dev); +EAPI void ecore_drm_output_free(Ecore_Drm_Output *output); +EAPI void ecore_drm_output_cursor_size_set(Ecore_Drm_Output *output, int handle, int w, int h); +EAPI Eina_Bool ecore_drm_output_enable(Ecore_Drm_Output *output); +EAPI void ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb); +EAPI void ecore_drm_output_repaint(Ecore_Drm_Output *output); + +EAPI Eina_Bool ecore_drm_inputs_create(Ecore_Drm_Device *dev); +EAPI void ecore_drm_inputs_destroy(Ecore_Drm_Device *dev); +EAPI Eina_Bool ecore_drm_inputs_enable(Ecore_Drm_Input *input); +EAPI void ecore_drm_inputs_disable(Ecore_Drm_Input *input); + +EAPI Eina_Bool ecore_drm_sprites_create(Ecore_Drm_Device *dev); +EAPI void ecore_drm_sprites_destroy(Ecore_Drm_Device *dev); +EAPI void ecore_drm_sprites_fb_set(Ecore_Drm_Sprite *sprite, int fb_id, int flags); +EAPI Eina_Bool ecore_drm_sprites_crtc_supported(Ecore_Drm_Output *output, unsigned int supported); + +EAPI Ecore_Drm_Fb *ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height); +EAPI void ecore_drm_fb_destroy(Ecore_Drm_Fb *fb); + +#endif diff --git a/src/lib/ecore_drm/ecore_drm.c b/src/lib/ecore_drm/ecore_drm.c new file mode 100644 index 0000000000..ac181e379e --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm.c @@ -0,0 +1,414 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "ecore_drm_private.h" +#include +#include +#include + +#define RIGHTS_LEN CMSG_LEN(sizeof(int)) + +#define IOVSET(_iov, _addr, _len) \ + (_iov)->iov_base = (void *)(_addr); \ + (_iov)->iov_len = (_len); + +/* local variables */ +static int _ecore_drm_init_count = 0; +static int _ecore_drm_sockets[2] = { -1, -1 }; +static struct cmsghdr *cmsgptr = NULL; + +/* external variables */ +struct udev *udev; +int _ecore_drm_log_dom = -1; +#ifdef LOG_TO_FILE +FILE *lg; +#endif + +static Eina_Bool +_ecore_drm_sockets_create(void) +{ + if (_ecore_drm_sockets[0] > -1) return EINA_TRUE; + + /* create a pair of sequenced sockets (fixed-length) + * NB: when reading from one of these, it is required that we read + * an entire packet with each read() call */ + if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK, + 0, _ecore_drm_sockets) < 0) + { + ERR("Socketpair Failed: %m"); + return EINA_FALSE; + } + + /* NB: We don't want cloexec for the sockets. That would cause them to be + * closed when we exec the child process but we need them open so that + * we can pass messages */ + /* if (fcntl(_ecore_drm_sockets[0], F_SETFD, FD_CLOEXEC) < 0) */ + /* { */ + /* ERR("Failed to set CLOEXEC: %m"); */ + /* return EINA_FALSE; */ + /* } */ + + /* DBG("Parent Socket: %d", _ecore_drm_sockets[0]); */ + /* DBG("Child Socket: %d", _ecore_drm_sockets[1]); */ + + return EINA_TRUE; +} + +static Eina_Bool +_ecore_drm_launcher_spawn(void) +{ + pid_t pid; + + if ((pid = fork()) < 0) return EINA_FALSE; + + if (pid == 0) + { + char renv[64], wenv[64], buff[PATH_MAX]; + char *args[1] = { NULL }; + sigset_t mask; + + /* read socket for slave is 1 */ + snprintf(renv, sizeof(renv), "ECORE_DRM_LAUNCHER_SOCKET_READ=%d", + _ecore_drm_sockets[1]); + + /* write socket for slave is 0 */ + snprintf(wenv, sizeof(wenv), "ECORE_DRM_LAUNCHER_SOCKET_WRITE=%d", + _ecore_drm_sockets[0]); + + /* assemble exec path */ + snprintf(buff, sizeof(buff), + "%s/ecore_drm/bin/%s/ecore_drm_launch", + PACKAGE_LIB_DIR, MODULE_ARCH); + + /* don't give our signal mask to the child */ + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGCHLD); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTTIN); + sigaddset(&mask, SIGTTOU); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + + /* NB: We need to use execve here so that capabilities are inherited. + * Also, this should set Our (ecore_drm) effective uid to be the + * owner of the launched process (setuid in this case) */ + char *ev[3] = { strdup(renv), strdup(wenv), NULL }; + execve(buff, args, ev); + } + else + { + int status; + + while (waitpid(pid, &status, WNOHANG) < 0) + if (errno != EINTR) break; + + return EINA_TRUE; + } + + return EINA_FALSE; +} + +static ssize_t +_ecore_drm_socket_send(int opcode, int fd, void *data, size_t bytes) +{ + Ecore_Drm_Message dmsg; + struct iovec iov[2]; + struct msghdr msg; + ssize_t size; + + /* Simplified version of sending messages. We don't need to send any + * 'credentials' with this as it is just basically an IPC to send over + * our request to the slave process */ + + /* NB: Hmm, don't think we need to set any socket options here */ + + memset(&dmsg, 0, sizeof(dmsg)); + + IOVSET(iov + 0, &dmsg, sizeof(dmsg)); + IOVSET(iov + 1, &data, bytes); + + dmsg.opcode = opcode; + dmsg.size = bytes; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_flags = 0; + + if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN)))) + return -1; + + cmsgptr->cmsg_level = SOL_SOCKET; + cmsgptr->cmsg_type = SCM_RIGHTS; + cmsgptr->cmsg_len = RIGHTS_LEN; + + msg.msg_control = cmsgptr; + msg.msg_controllen = RIGHTS_LEN; + + if (fd > -1) + *((int *)CMSG_DATA(cmsgptr)) = fd; + else + *((int *)CMSG_DATA(cmsgptr)) = _ecore_drm_sockets[1]; + + errno = 0; + size = sendmsg(_ecore_drm_sockets[1], &msg, MSG_EOR); + if (errno != 0) + { + DBG("Error Sending Message: %m"); + } + + /* DBG("Sent %li bytes to Socket %d", size, _ecore_drm_sockets[1]); */ + + return size; +} + +static int +_ecore_drm_socket_receive(int opcode EINA_UNUSED, int *fd, void **data, size_t bytes) +{ + int ret = ECORE_DRM_OP_FAILURE; + Ecore_Drm_Message dmsg; + struct cmsghdr *cmsg; + struct iovec iov[2]; + struct msghdr msg; + char buff[CMSG_SPACE(sizeof(fd))]; + /* ssize_t size; */ + + memset(&dmsg, 0, sizeof(dmsg)); + memset(&buff, 0, sizeof(buff)); + + IOVSET(iov + 0, &dmsg, sizeof(dmsg)); + IOVSET(iov + 1, &buff, sizeof(buff)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_flags = 0; + + if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN)))) + return -1; + + msg.msg_control = cmsgptr; + msg.msg_controllen = RIGHTS_LEN; + + errno = 0; + recvmsg(_ecore_drm_sockets[0], &msg, 0); + if (errno != 0) + { + ERR("Failed to receive message: %m"); + return -1; + } + + /* DBG("Received %li bytes from %d", size, _ecore_drm_sockets[0]); */ + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + + switch (cmsg->cmsg_type) + { + case SCM_RIGHTS: + if (fd) *fd = *((int *)CMSG_DATA(cmsg)); + switch (dmsg.opcode) + { + case ECORE_DRM_OP_DEVICE_OPEN: + case ECORE_DRM_OP_DEVICE_CLOSE: + case ECORE_DRM_OP_TTY_OPEN: + case ECORE_DRM_OP_DEVICE_MASTER_DROP: + case ECORE_DRM_OP_DEVICE_MASTER_SET: + if ((fd) && (*fd >= 0)) ret = ECORE_DRM_OP_SUCCESS; + if (data) memcpy(*data, buff, bytes); + break; + default: + break; + } + break; + default: + break; + } + } + + return ret; +} + +void +_ecore_drm_message_send(int opcode, int fd, void *data, size_t bytes) +{ + _ecore_drm_socket_send(opcode, fd, data, bytes); +} + +Eina_Bool +_ecore_drm_message_receive(int opcode, int *fd, void **data, size_t bytes) +{ + int ret = ECORE_DRM_OP_FAILURE; + + ret = _ecore_drm_socket_receive(opcode, fd, data, bytes); + if (ret != ECORE_DRM_OP_SUCCESS) return EINA_FALSE; + + return EINA_TRUE; +} + +/** + * @defgroup Ecore_Drm_Init_Group Drm Library Init and Shutdown Functions + * + * Functions that start and shutdown the Ecore_Drm Library. + */ + +/** + * Initialize the Ecore_Drm library + * + * @return The number of times the library has been initialized without + * being shut down. 0 is returned if an error occurs. + * + * @ingroup Ecore_Drm_Init_Group + */ +EAPI int +ecore_drm_init(void) +{ + /* if we have already initialized, return the count */ + if (++_ecore_drm_init_count != 1) return _ecore_drm_init_count; + + /* try to init eina */ + if (!eina_init()) return --_ecore_drm_init_count; + + if (!ecore_init()) + { + eina_shutdown(); + return --_ecore_drm_init_count; + } + + /* set logging level */ + eina_log_level_set(EINA_LOG_LEVEL_DBG); + + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + /* optionally log output to a file */ +#ifdef LOG_TO_FILE + int log_fd; + char log_path[PATH_MAX]; + mode_t um; + + /* assemble logging file path */ + strcpy(log_path, "/tmp/ecore_drm_XXXXXX"); + + /* create temporary logging file */ + um = umask(S_IRWXG | S_IRWXO); + log_fd = mkstemp(log_path); + umask(um); + + /* try to open logging file */ + if (!(lg = fdopen(log_fd, "w"))) + goto log_err; + + eina_log_print_cb_set(eina_log_print_cb_file, lg); +#endif + + /* try to create logging domain */ + _ecore_drm_log_dom = + eina_log_domain_register("ecore_drm", ECORE_DRM_DEFAULT_LOG_COLOR); + if (!_ecore_drm_log_dom) + { + EINA_LOG_ERR("Could not create log domain for Ecore_Drm"); + goto log_err; + } + + /* set default logging level for this domain */ + if (!eina_log_domain_level_check(_ecore_drm_log_dom, EINA_LOG_LEVEL_DBG)) + eina_log_domain_level_set("ecore_drm", EINA_LOG_LEVEL_DBG); + + /* try to init udev */ + if (!(udev = udev_new())) + goto udev_err; + + /* try to create the socketpair */ + if (!_ecore_drm_sockets_create()) + goto sock_err; + + /* try to run Spartacus */ + if (!_ecore_drm_launcher_spawn()) + goto spawn_err; + + /* return init count */ + return _ecore_drm_init_count; + +spawn_err: + close(_ecore_drm_sockets[0]); + close(_ecore_drm_sockets[1]); +sock_err: + if (udev) udev_unref(udev); +udev_err: + ecore_shutdown(); + eina_log_domain_unregister(_ecore_drm_log_dom); + _ecore_drm_log_dom = -1; +log_err: +#ifdef LOG_TO_FILE + if (lg) fclose(lg); +#endif + + /* shutdown eina */ + eina_shutdown(); + + return --_ecore_drm_init_count; +} + +/** + * Shutdown the Ecore_Drm library. + * + * @return The number of times the library has been initialized without + * being shutdown. 0 is returned if an error occurs. + * + * @ingroup Ecore_Drm_Init_Group + */ +EAPI int +ecore_drm_shutdown(void) +{ + /* if we are still in use, decrement init count and get out */ + if (--_ecore_drm_init_count != 0) return _ecore_drm_init_count; + + /* close udev handle */ + if (udev) udev_unref(udev); + + /* close sockets */ + close(_ecore_drm_sockets[0]); + close(_ecore_drm_sockets[1]); + + ecore_shutdown(); + + /* unregsiter log domain */ + eina_log_domain_unregister(_ecore_drm_log_dom); + _ecore_drm_log_dom = -1; + +#ifdef LOG_TO_FILE + if (lg) fclose(lg); +#endif + + /* shutdown eina */ + eina_shutdown(); + + /* return init count */ + return _ecore_drm_init_count; +} + +EAPI void * +ecore_drm_gbm_get(Ecore_Drm_Device *dev) +{ + if (!dev) return NULL; + +#ifdef HAVE_GBM + return dev->gbm; +#endif + + return NULL; +} + +EAPI unsigned int +ecore_drm_gbm_format_get(Ecore_Drm_Device *dev) +{ + if (!dev) return 0; + return dev->format; +} diff --git a/src/lib/ecore_drm/ecore_drm_device.c b/src/lib/ecore_drm/ecore_drm_device.c new file mode 100644 index 0000000000..3fec7f659c --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm_device.c @@ -0,0 +1,583 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "ecore_drm_private.h" +#include + +/* #ifdef HAVE_GBM */ +/* static Eina_Bool */ +/* _ecore_drm_device_egl_config_get(Ecore_Drm_Device *dev, const EGLint *attribs, const EGLint *visual) */ +/* { */ +/* EGLint c = 0, m = 0; */ +/* EGLConfig *cfgs; */ +/* int i = 0; */ + +/* if (!eglGetConfigs(dev->egl.disp, NULL, 0, &c) || (c < 1)) */ +/* return EINA_FALSE; */ + +/* if (!(cfgs = calloc(c, sizeof(*cfgs)))) return EINA_FALSE; */ + +/* if (!eglChooseConfig(dev->egl.disp, attribs, cfgs, c, &m)) */ +/* { */ +/* free(cfgs); */ +/* return EINA_FALSE; */ +/* } */ + +/* for (i = 0; i < m; i++) */ +/* { */ +/* EGLint id; */ + +/* if (visual) */ +/* { */ +/* if (!eglGetConfigAttrib(dev->egl.disp, cfgs[i], */ +/* EGL_NATIVE_VISUAL_ID, &id)) */ +/* continue; */ + +/* if ((id != 0) && (id != *visual)) */ +/* continue; */ +/* } */ + +/* dev->egl.cfg = cfgs[i]; */ +/* free(cfgs); */ +/* return EINA_TRUE; */ +/* } */ + +/* free(cfgs); */ +/* return EINA_FALSE; */ +/* } */ +/* #endif */ + +static void +_ecore_drm_device_cb_page_flip(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data) +{ + Ecore_Drm_Output *output; + + DBG("Drm Page Flip Event"); + + if (!(output = data)) return; + + if (output->pending_flip) + { + ecore_drm_output_fb_release(output, output->current); + output->current = output->next; + output->next = NULL; + } + + output->pending_flip = EINA_FALSE; + if (!output->pending_vblank) ecore_drm_output_repaint(output); +} + +static void +_ecore_drm_device_cb_vblank(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data) +{ + Ecore_Drm_Sprite *sprite; + Ecore_Drm_Output *output; + + DBG("Drm VBlank Event"); + + if (!(sprite = data)) return; + + output = sprite->output; + output->pending_vblank = EINA_FALSE; + + ecore_drm_output_fb_release(output, sprite->current_fb); + sprite->current_fb = sprite->next_fb; + sprite->next_fb = NULL; + + if (!output->pending_flip) _ecore_drm_output_frame_finish(output); +} + +static Eina_Bool +_ecore_drm_device_cb_event(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED) +{ + Ecore_Drm_Device *dev; + drmEventContext ctx; + + if (!(dev = data)) return ECORE_CALLBACK_RENEW; + + DBG("Drm Device Event"); + + memset(&ctx, 0, sizeof(ctx)); + + ctx.version = DRM_EVENT_CONTEXT_VERSION; + ctx.page_flip_handler = _ecore_drm_device_cb_page_flip; + ctx.vblank_handler = _ecore_drm_device_cb_vblank; + + drmHandleEvent(dev->drm.fd, &ctx); + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_ecore_drm_device_cb_idle(void *data) +{ + Ecore_Drm_Device *dev; + Ecore_Drm_Output *output; + Eina_List *l; + + if (!(dev = data)) return ECORE_CALLBACK_CANCEL; + + EINA_LIST_FOREACH(dev->outputs, l, output) + { + output->need_repaint = EINA_TRUE; + if (output->repaint_scheduled) continue; + _ecore_drm_output_repaint_start(output); + } + + return ECORE_CALLBACK_RENEW; +} + +/** + * @defgroup Ecore_Drm_Device_Group Device manipulation functions + * + * Functions that deal with finding, opening, closing, and otherwise using + * the DRM device itself. + */ + +/** + * Find a drm device in the system. + * + * @param name The name of the device to find. If NULL, this function will + * search for the default drm device. + * @param seat The name of the seat where this device may be found. If NULL, + * this function will use a default seat name 'seat0'. + * + * @return An opaque Ecore_Drm_Device structure representing the card. + * + * @ingroup Ecore_Drm_Device_Group + */ +EAPI Ecore_Drm_Device * +ecore_drm_device_find(const char *name, const char *seat) +{ + Ecore_Drm_Device *dev = NULL; + struct udev_enumerate *uenum; + struct udev_list_entry *uentry; + struct udev_device *udevice, *tmpdevice = NULL, *pcidevice; + const char *path = NULL, *devseat = NULL; + + /* check for existing udev reference */ + if (!udev) return NULL; + + /* setup udev enumerator */ + uenum = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(uenum, "drm"); + udev_enumerate_add_match_subsystem(uenum, "card[0-9]*"); + + /* ask udev for list of drm devices */ + udev_enumerate_scan_devices(uenum); + + /* loop list of returned devices */ + udev_list_entry_foreach(uentry, udev_enumerate_get_list_entry(uenum)) + { + /* get device path */ + path = udev_list_entry_get_name(uentry); + + /* get udev device */ + if (!(udevice = udev_device_new_from_syspath(udev, path))) + continue; + + /* if we are looking for a certain device, then compare names */ + if (name) + { + if (strcmp(name, udev_device_get_devnode(udevice))) + { + udev_device_unref(udevice); + continue; + } + } + + /* get this devices' seat */ + devseat = udev_device_get_property_value(udevice, "ID_SEAT"); + if (!devseat) devseat = "seat0"; + + /* if we are looking for a device on a certain seat, compare it */ + if (seat) + { + if (strcmp(seat, devseat)) + { + udev_device_unref(udevice); + continue; + } + } + else + { + /* no seat name passed to use. check default */ + if (strcmp(devseat, "seat0")) + { + udev_device_unref(udevice); + continue; + } + } + + /* try to find the boot_vga attribute */ + if ((pcidevice = + udev_device_get_parent_with_subsystem_devtype(udevice, "pci", NULL))) + { + const char *id; + + if ((id = udev_device_get_sysattr_value(pcidevice, "boot_vga"))) + { + if (!strcmp(id, "1")) + { + if (tmpdevice) udev_device_unref(tmpdevice); + tmpdevice = udevice; + break; + } + } + } + + if (!tmpdevice) + tmpdevice = udevice; + else + udev_device_unref(udevice); + } + + /* destroy the enumerator */ + udev_enumerate_unref(uenum); + + if (tmpdevice) + { + DBG("Found Drm Device"); + DBG("\tFilename: %s", udev_device_get_devnode(tmpdevice)); + DBG("\tDriver: %s", udev_device_get_driver(tmpdevice)); + DBG("\tDevpath: %s", udev_device_get_devpath(tmpdevice)); + DBG("\tSyspath: %s", udev_device_get_syspath(tmpdevice)); + DBG("\tSysname: %s", udev_device_get_sysname(tmpdevice)); + + /* try to allocate space for return device structure */ + if ((dev = calloc(1, sizeof(Ecore_Drm_Device)))) + { + const char *id, *seat; + + /* set device name */ + dev->drm.name = + eina_stringshare_add(udev_device_get_devnode(tmpdevice)); + + /* set device path */ + dev->drm.path = + eina_stringshare_add(udev_device_get_syspath(tmpdevice)); + + /* store id for this device */ + if ((id = udev_device_get_sysnum(tmpdevice))) + dev->id = atoi(id); + + /* set dev seat */ + seat = udev_device_get_property_value(tmpdevice, "ID_SEAT"); + if (!seat) seat = "seat0"; + + dev->seat = eina_stringshare_add(seat); + + dev->format = GBM_FORMAT_XRGB8888; + dev->use_hw_accel = EINA_FALSE; + } + } + + /* release device reference */ + udev_device_unref(tmpdevice); + + return dev; +} + +/** + * Free an Ecore_Drm_Device + * + * This function will cleanup and free any previously allocated Ecore_Drm_Device. + * + * @param dev The Ecore_Drm_Device to free + * + * @ingroup Ecore_Drm_Device_Group + */ +EAPI void +ecore_drm_device_free(Ecore_Drm_Device *dev) +{ + Ecore_Drm_Output *output; + + /* check for valid device */ + if (!dev) return; + + /* free outputs */ + EINA_LIST_FREE(dev->outputs, output) + ecore_drm_output_free(output); + + /* free crtcs */ + if (dev->crtcs) free(dev->crtcs); + + /* free device name */ + if (dev->drm.name) eina_stringshare_del(dev->drm.name); + + /* free device path */ + if (dev->drm.path) eina_stringshare_del(dev->drm.path); + + /* free device seat */ + if (dev->seat) eina_stringshare_del(dev->seat); + + /* free structure */ + free(dev); +} + +/** + * Open an Ecore_Drm_Device + * + * This function will open an existing Ecore_Drm_Device for use. + * + * @param dev The Ecore_Drm_Device to try and open + * + * @return EINA_TRUE on success, EINA_FALSE on failure + * + * @ingroup Ecore_Drm_Device_Group + */ +EAPI Eina_Bool +ecore_drm_device_open(Ecore_Drm_Device *dev) +{ + uint64_t caps; + + /* check for valid device */ + if ((!dev) || (!dev->drm.name)) return EINA_FALSE; + + dev->drm.fd = open(dev->drm.name, O_RDWR); + if (dev->drm.fd < 0) return EINA_FALSE; + + DBG("Opened Device %s : %d", dev->drm.name, dev->drm.fd); + + if (!drmGetCap(dev->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &caps)) + { + if (caps == 1) + dev->drm.clock = CLOCK_MONOTONIC; + else + dev->drm.clock = CLOCK_REALTIME; + } + else + { + ERR("Could not get device capabilities: %m"); + } + +/* #ifdef HAVE_GBM */ +/* if (getenv("ECORE_DRM_HW_ACCEL")) */ +/* { */ + /* Typically, gbm loads the dri driver However some versions of Mesa + * do not have libglapi symbols linked in the driver. Because of this, + * using hardware accel for our drm code Could fail with a + * message that the driver could not load. Let's be proactive and + * work around this for the user by preloading the glapi library */ + /* dlopen("libglapi.so.0", (RTLD_LAZY | RTLD_GLOBAL)); */ + + /* if ((dev->gbm = gbm_create_device(dev->drm.fd))) */ + /* { */ + /* EGLint major, minor, visual; */ + /* const EGLint attribs[] = */ + /* { */ + /* EGL_SURFACE_TYPE, EGL_WINDOW_BIT, */ + /* EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, */ + /* EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, */ + /* EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE */ + /* }; */ + + /* dev->use_hw_accel = EINA_TRUE; */ + /* dev->format = GBM_FORMAT_XRGB8888; */ + + /* dev->egl.disp = eglGetDisplay(dev->gbm); */ + /* if (dev->egl.disp == EGL_NO_DISPLAY) */ + /* { */ + /* ERR("Could not get egl display"); */ + /* goto init_software; */ + /* } */ + + /* if (!eglInitialize(dev->egl.disp, &major, &minor)) */ + /* { */ + /* ERR("Could not initialize egl"); */ + /* goto init_software; */ + /* } */ + + /* visual = dev->format; */ + /* if (!_ecore_drm_device_egl_config_get(dev, attribs, &visual)) */ + /* { */ + /* ERR("Could not get egl config"); */ + /* goto init_software; */ + /* } */ + /* } */ + /* else */ + /* { */ + /* WRN("Failed to create gbm device"); */ + /* goto init_software; */ + /* } */ + /* } */ + /* else */ +/* #endif */ + /* { */ + /* TODO: init software */ +/* init_software: */ +/* DBG("Init Software Engine"); */ +/* #ifdef HAVE_GBM */ +/* if (dev->egl.disp) */ +/* { */ +/* eglMakeCurrent(dev->egl.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, */ +/* EGL_NO_CONTEXT); */ +/* eglTerminate(dev->egl.disp); */ +/* eglReleaseThread(); */ +/* } */ + +/* if (dev->gbm) gbm_device_destroy(dev->gbm); */ +/* dev->gbm = NULL; */ +/* #endif */ +/* } */ + + dev->drm.hdlr = + ecore_main_fd_handler_add(dev->drm.fd, ECORE_FD_READ, + _ecore_drm_device_cb_event, dev, NULL, NULL); + + dev->drm.idler = + ecore_idle_enterer_add(_ecore_drm_device_cb_idle, dev); + + return EINA_TRUE; +} + +/** + * Close an Ecore_Drm_Device + * + * This function will close a previously opened Ecore_Drm_Device + * + * @param dev The Ecore_Drm_Device to free + * + * @return EINA_TRUE on success, EINA_FALSE on failure + * + * @ingroup Ecore_Drm_Device_Group + */ +EAPI Eina_Bool +ecore_drm_device_close(Ecore_Drm_Device *dev) +{ + /* check for valid device */ + if (!dev) return EINA_FALSE; + +/* #ifdef HAVE_GBM */ +/* if (dev->use_hw_accel) */ +/* { */ +/* if (dev->egl.disp) */ +/* { */ +/* eglMakeCurrent(dev->egl.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, */ +/* EGL_NO_CONTEXT); */ +/* eglTerminate(dev->egl.disp); */ +/* eglReleaseThread(); */ +/* } */ + +/* if (dev->gbm) gbm_device_destroy(dev->gbm); */ +/* dev->gbm = NULL; */ +/* } */ +/* #endif */ + + if (dev->drm.hdlr) ecore_main_fd_handler_del(dev->drm.hdlr); + dev->drm.hdlr = NULL; + + close(dev->drm.fd); + + /* reset device fd */ + dev->drm.fd = -1; + + /* free(data); */ + + return EINA_TRUE; +} + +/** + * Get if a given Ecore_Drm_Device is master + * + * This function will check if the given drm device is set to master + * + * @param dev The Ecore_Drm_Device to check + * + * @return EINA_TRUE if device is master, EINA_FALSE otherwise + * + * @ingroup Ecore_Drm_Device_Group + */ +EAPI Eina_Bool +ecore_drm_device_master_get(Ecore_Drm_Device *dev) +{ + /* drm_magic_t mag; */ + + /* check for valid device */ + if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE; + + /* FIXME: Remote this to the slave process !! */ + + /* get if we are master or not */ + /* if ((drmGetMagic(dev->drm.fd, &mag) == 0) && */ + /* (drmAuthMagic(dev->drm.fd, mag) == 0)) */ + /* return EINA_TRUE; */ + + return EINA_FALSE; +} + +/** + * Set a given Ecore_Drm_Device to master + * + * This function will attempt to set a given drm device to be master + * + * @param dev The Ecore_Drm_Device to set + * + * @return EINA_TRUE on success, EINA_FALSE on failure + * + * @ingroup Ecore_Drm_Device_Group + */ +EAPI Eina_Bool +ecore_drm_device_master_set(Ecore_Drm_Device *dev) +{ + Eina_Bool ret = EINA_FALSE; + int dfd; + + /* check for valid device */ + if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE; + + DBG("Set Master On Fd: %d", dev->drm.fd); + + /* try to close the device */ + _ecore_drm_message_send(ECORE_DRM_OP_DEVICE_MASTER_SET, dev->drm.fd, + NULL, 0); + + /* get the result of the close operation */ + ret = _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_MASTER_SET, &dfd, + NULL, 0); + if (!ret) return EINA_FALSE; + + return EINA_TRUE; +} + +/** + * Tell a given Ecore_Drm_Device to stop being master + * + * This function will attempt to ask a drm device to stop being master + * + * @param dev The Ecore_Drm_Device to set + * + * @return EINA_TRUE on success, EINA_FALSE on failure + * + * @ingroup Ecore_Drm_Device_Group + */ +EAPI Eina_Bool +ecore_drm_device_master_drop(Ecore_Drm_Device *dev) +{ + Eina_Bool ret = EINA_FALSE; + int dfd; + + /* check for valid device */ + if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE; + + DBG("Drop Master On Fd: %d", dev->drm.fd); + + /* try to close the device */ + _ecore_drm_message_send(ECORE_DRM_OP_DEVICE_MASTER_DROP, dev->drm.fd, + NULL, 0); + + /* get the result of the close operation */ + ret = _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_MASTER_DROP, &dfd, + NULL, 0); + if (!ret) return EINA_FALSE; + + return EINA_TRUE; +} + +EAPI int +ecore_drm_device_fd_get(Ecore_Drm_Device *dev) +{ + if (!dev) return -1; + return dev->drm.fd; +} diff --git a/src/lib/ecore_drm/ecore_drm_evdev.c b/src/lib/ecore_drm/ecore_drm_evdev.c new file mode 100644 index 0000000000..0e5e840056 --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm_evdev.c @@ -0,0 +1,360 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +/* copied from udev/extras/input_id/input_id.c */ +/* we must use this kernel-compatible implementation */ +#define BITS_PER_LONG (sizeof(unsigned long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<> OFF(bit)) & 1) +/* end copied */ + +#include "ecore_drm_private.h" +#include +#include + +/* local functions */ +static Eina_Bool +_device_configure(Ecore_Drm_Evdev *edev) +{ + Eina_Bool ret = EINA_FALSE; + + if (!edev) return EINA_FALSE; + + if ((edev->caps & (EVDEV_MOTION_ABS | EVDEV_MOTION_REL)) && + (edev->caps & EVDEV_BUTTON)) + { + DBG("Input device %s is a pointer", edev->name); + edev->seat_caps |= EVDEV_SEAT_POINTER; + ret = EINA_TRUE; + } + + if (edev->caps & EVDEV_KEYBOARD) + { + DBG("Input device %s is a keyboard", edev->name); + edev->seat_caps |= EVDEV_SEAT_KEYBOARD; + ret = EINA_TRUE; + } + + if (edev->caps & EVDEV_TOUCH) + { + DBG("Input device %s is a touchpad", edev->name); + edev->seat_caps |= EVDEV_SEAT_TOUCH; + ret = EINA_TRUE; + } + + return ret; +} + +static Eina_Bool +_device_handle(Ecore_Drm_Evdev *edev) +{ + struct input_absinfo absinfo; + unsigned long dev_bits[NBITS(EV_MAX)]; + unsigned long abs_bits[NBITS(ABS_MAX)]; + unsigned long rel_bits[NBITS(REL_MAX)]; + unsigned long key_bits[NBITS(KEY_MAX)]; + /* Eina_Bool have_key = EINA_FALSE; */ + Eina_Bool have_abs = EINA_FALSE; + + if (!edev) return EINA_FALSE; + + ioctl(edev->fd, EVIOCGBIT(0, sizeof(dev_bits)), dev_bits); + if (TEST_BIT(dev_bits, EV_ABS)) + { + have_abs = EINA_TRUE; + + ioctl(edev->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits); + + if ((TEST_BIT(abs_bits, ABS_WHEEL)) || + (TEST_BIT(abs_bits, ABS_GAS)) || + (TEST_BIT(abs_bits, ABS_BRAKE)) || + (TEST_BIT(abs_bits, ABS_HAT0X))) + { + /* ignore joystick */ + return EINA_FALSE; + } + + if (TEST_BIT(abs_bits, ABS_X)) + { + ioctl(edev->fd, EVIOCGABS(ABS_X), &absinfo); + edev->abs.min_x = absinfo.minimum; + edev->abs.max_x = absinfo.maximum; + edev->caps |= EVDEV_MOTION_ABS; + } + + if (TEST_BIT(abs_bits, ABS_Y)) + { + ioctl(edev->fd, EVIOCGABS(ABS_Y), &absinfo); + edev->abs.min_y = absinfo.minimum; + edev->abs.max_y = absinfo.maximum; + edev->caps |= EVDEV_MOTION_ABS; + } + + if ((TEST_BIT(abs_bits, ABS_MT_POSITION_X)) && + (TEST_BIT(abs_bits, ABS_MT_POSITION_Y))) + { + DBG("Handle MultiTouch Device: %s", edev->path); + } + } + + if (TEST_BIT(dev_bits, EV_REL)) + { + ioctl(edev->fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), rel_bits); + + if ((TEST_BIT(rel_bits, REL_X)) || (TEST_BIT(rel_bits, REL_Y))) + edev->caps |= EVDEV_MOTION_REL; + } + + if (TEST_BIT(dev_bits, EV_KEY)) + { + unsigned int i = 0; + + /* have_key = EINA_TRUE; */ + + ioctl(edev->fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits); + + if (have_abs) + { + if ((TEST_BIT(key_bits, BTN_TOOL_FINGER)) && + (!TEST_BIT(key_bits, BTN_TOOL_PEN))) + { + DBG("Device Is Touchpad: %s", edev->path); + } + } + + for (i = KEY_ESC; i < KEY_MAX; i++) + { + if ((i >= BTN_MISC) && (i < KEY_OK)) continue; + if (TEST_BIT(key_bits, i)) + { + edev->caps |= EVDEV_KEYBOARD; + break; + } + } + + if (TEST_BIT(key_bits, BTN_TOUCH)) + edev->caps |= EVDEV_TOUCH; + + for (i = BTN_MISC; i < BTN_JOYSTICK; i++) + { + if (TEST_BIT(key_bits, i)) + { + edev->caps |= EVDEV_BUTTON; + edev->caps &= ~EVDEV_TOUCH; + break; + } + } + } + + if (TEST_BIT(dev_bits, EV_LED)) edev->caps |= EVDEV_KEYBOARD; + + return EINA_TRUE; +} + +static void +_device_notify_key(Ecore_Drm_Evdev *dev, struct input_event *event, unsigned int timestamp) +{ + DBG("Key Event"); + DBG("\tCode: %d", event->code); + DBG("\tValue: %d", event->value); + + if ((event->code >= KEY_ESC) && (event->code <= KEY_COMPOSE)) + { + /* ignore key repeat */ + if (event->value == 2) + { + DBG("\tKey Repeat"); + } + } +} + +static void +_device_process_flush(Ecore_Drm_Evdev *dev, unsigned int timestamp) +{ + switch (dev->pending_event) + { + case EVDEV_NONE: + return; + case EVDEV_RELATIVE_MOTION: + goto out; + break; + case EVDEV_ABSOLUTE_MT_DOWN: + goto out; + break; + case EVDEV_ABSOLUTE_MT_MOTION: + goto out; + break; + case EVDEV_ABSOLUTE_MT_UP: + goto out; + break; + case EVDEV_ABSOLUTE_TOUCH_DOWN: + goto out; + break; + case EVDEV_ABSOLUTE_MOTION: + goto out; + break; + case EVDEV_ABSOLUTE_TOUCH_UP: + goto out; + break; + } + +out: + dev->pending_event = EVDEV_NONE; +} + +static void +_device_process_key(Ecore_Drm_Evdev *dev, struct input_event *event, unsigned int timestamp) +{ + if (event->code == BTN_TOUCH) + { + /* TODO: check for mt device */ + return; + } + + _device_process_flush(dev, timestamp); + + switch (event->code) + { + case BTN_LEFT: + case BTN_RIGHT: + case BTN_MIDDLE: + case BTN_SIDE: + case BTN_EXTRA: + case BTN_FORWARD: + case BTN_BACK: + case BTN_TASK: + /* TODO: notify button */ + break; + default: + _device_notify_key(dev, event, timestamp); + break; + } +} + +static void +_device_process(Ecore_Drm_Evdev *dev, struct input_event *event, int count) +{ + struct input_event *ev, *end; + unsigned int timestamp = 0; + + DBG("Evdev Device Process"); + + ev = event; + end = ev + count; + for (ev = event; ev < end; ev++) + { + timestamp = (ev->time.tv_sec * 1000) + (ev->time.tv_usec / 1000); + + switch (ev->type) + { + case EV_KEY: + _device_process_key(dev, ev, timestamp); + break; + case EV_REL: + DBG("\tRelative Event"); + break; + case EV_ABS: + DBG("\tAbsolute Event"); + break; + case EV_SYN: + _device_process_flush(dev, timestamp); + break; + default: + break; + } + } +} + +static Eina_Bool +_cb_device_data(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED) +{ + Ecore_Drm_Evdev *edev; + struct input_event ev[32]; + int len = 0; + + if (!(edev = data)) return EINA_TRUE; + + do + { + len = read(edev->fd, &ev, sizeof(ev)); + + if ((len < 0) || ((len % sizeof(ev[0])) != 0)) + { + if ((len < 0) && (errno != EAGAIN) && (errno != EINTR)) + { + ERR("Device Died"); + } + + return EINA_TRUE; + } + + edev->event_process(edev, ev, (len / sizeof(ev[0]))); + + } while (len > 0); + + return EINA_TRUE; +} + +/* external functions */ +Ecore_Drm_Evdev * +_ecore_drm_evdev_device_create(Ecore_Drm_Seat *seat, const char *path, int fd) +{ + Ecore_Drm_Evdev *edev; + char name[256] = "unknown"; + + if (!(edev = calloc(1, sizeof(Ecore_Drm_Evdev)))) + return NULL; + + edev->seat = seat; + edev->path = eina_stringshare_add(path); + edev->fd = fd; + + if (ioctl(edev->fd, EVIOCGNAME(sizeof(name)), name) < 0) + DBG("Error getting device name: %m"); + + name[sizeof(name) - 1] = '\0'; + edev->name = eina_stringshare_add(name); + + if (!_device_handle(edev)) + { + ERR("Unhandled Input Device: %s", name); + _ecore_drm_evdev_device_destroy(edev); + return NULL; + } + + if (!_device_configure(edev)) + { + _ecore_drm_evdev_device_destroy(edev); + return NULL; + } + + edev->event_process = _device_process; + + edev->hdlr = + ecore_main_fd_handler_add(edev->fd, ECORE_FD_READ, + _cb_device_data, edev, NULL, NULL); + if (!edev->hdlr) + { + ERR("Could not create fd handler"); + _ecore_drm_evdev_device_destroy(edev); + return NULL; + } + + return edev; +} + +void +_ecore_drm_evdev_device_destroy(Ecore_Drm_Evdev *dev) +{ + if (!dev) return; + + if (dev->path) eina_stringshare_del(dev->path); + if (dev->name) eina_stringshare_del(dev->name); + if (dev->hdlr) ecore_main_fd_handler_del(dev->hdlr); + + free(dev); +} diff --git a/src/lib/ecore_drm/ecore_drm_fb.c b/src/lib/ecore_drm/ecore_drm_fb.c new file mode 100644 index 0000000000..40dd473ed9 --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm_fb.c @@ -0,0 +1,151 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "ecore_drm_private.h" + +/** + * @defgroup Ecore_Drm_Fb_Group + * + */ + +/* TODO: DOXY !! */ + +EAPI Ecore_Drm_Fb * +ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height) +{ + Ecore_Drm_Fb *fb; + struct drm_mode_create_dumb carg; + struct drm_mode_destroy_dumb darg; + struct drm_mode_map_dumb marg; + + if (!(fb = calloc(1, sizeof(Ecore_Drm_Fb)))) return NULL; + + memset(&carg, 0, sizeof(struct drm_mode_create_dumb)); + + carg.bpp = 32; // FIXME: Hard-coded depth + carg.width = width; + carg.height = height; + + if (drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &carg)) + { + ERR("Could not create dumb framebuffer: %m"); + goto create_err; + } + + fb->from_client = EINA_TRUE; + fb->hdl = carg.handle; + fb->stride = carg.pitch; + fb->size = carg.size; + fb->fd = dev->drm.fd; + + if (drmModeAddFB(dev->drm.fd, width, height, 24, 32, + fb->stride, fb->hdl, &fb->id)) + { + ERR("Could not add framebuffer: %m"); + goto add_err; + } + + memset(&marg, 0, sizeof(struct drm_mode_map_dumb)); + marg.handle = fb->hdl; + if (drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_MAP_DUMB, &marg)) + { + ERR("Could not map framebuffer: %m"); + goto map_err; + } + + fb->mmap = + mmap(0, fb->size, PROT_WRITE | PROT_READ, MAP_SHARED, + dev->drm.fd, marg.offset); + if (fb->mmap == MAP_FAILED) + { + ERR("Could not mmap framebuffer space: %m"); + goto map_err; + } + + memset(fb->mmap, 0, fb->size); + + return fb; + +map_err: + drmModeRmFB(fb->fd, fb->id); +add_err: + memset(&darg, 0, sizeof(struct drm_mode_destroy_dumb)); + darg.handle = fb->hdl; + drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg); +create_err: + free(fb); + return NULL; +} + +EAPI void +ecore_drm_fb_destroy(Ecore_Drm_Fb *fb) +{ + struct drm_mode_destroy_dumb darg; + + if ((!fb) || (!fb->mmap)) return; + + if (fb->id) drmModeRmFB(fb->fd, fb->id); + munmap(fb->mmap, fb->size); + memset(&darg, 0, sizeof(struct drm_mode_destroy_dumb)); + darg.handle = fb->hdl; + drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg); + free(fb); +} + +/* #ifdef HAVE_GBM */ +/* static void */ +/* _ecore_drm_fb_user_data_destroy(struct gbm_bo *bo EINA_UNUSED, void *data) */ +/* { */ +/* Ecore_Drm_Fb *fb; */ + +/* if (!(fb = data)) return; */ +/* ecore_drm_fb_destroy(fb); */ +/* } */ + +/* Ecore_Drm_Fb * */ +/* _ecore_drm_fb_bo_get(Ecore_Drm_Device *dev, struct gbm_bo *bo) */ +/* { */ +/* Ecore_Drm_Fb *fb; */ +/* unsigned int width, height; */ +/* unsigned int h[4], p[4], o[4]; */ +/* int ret = -1; */ + +/* if ((fb = gbm_bo_get_user_data(bo))) return fb; */ + +/* if (!(fb = calloc(1, sizeof(Ecore_Drm_Fb)))) return NULL; */ + +/* fb->bo = bo; */ + +/* width = gbm_bo_get_width(bo); */ +/* height = gbm_bo_get_height(bo); */ +/* fb->stride = gbm_bo_get_stride(bo); */ +/* fb->hdl = gbm_bo_get_handle(bo).u32; */ +/* fb->size = (fb->stride * height); */ +/* fb->fd = dev->drm.fd; */ + +/* h[0] = fb->hdl; */ +/* p[0] = fb->stride; */ +/* o[0] = 0; */ + +/* ret = drmModeAddFB2(dev->drm.fd, width, height, dev->format, h, p, o, */ +/* &fb->id, 0); */ +/* if (ret) */ +/* { */ +/* ret = drmModeAddFB(dev->drm.fd, width, height, 24, 32, */ +/* fb->stride, fb->hdl, &fb->id); */ + +/* } */ + +/* if (ret) */ +/* { */ +/* ERR("Error during ModeAddFb"); */ +/* free(fb); */ +/* return NULL; */ +/* } */ + +/* gbm_bo_set_user_data(bo, fb, _ecore_drm_fb_user_data_destroy); */ + +/* return fb; */ +/* } */ +/* #endif */ diff --git a/src/lib/ecore_drm/ecore_drm_inputs.c b/src/lib/ecore_drm/ecore_drm_inputs.c new file mode 100644 index 0000000000..c511d86656 --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm_inputs.c @@ -0,0 +1,303 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "ecore_drm_private.h" + +/* local functions */ +static Ecore_Drm_Seat * +_seat_get(Ecore_Drm_Input *input, const char *seat) +{ + Ecore_Drm_Seat *s; + Eina_List *l; + + EINA_LIST_FOREACH(input->dev->seats, l, s) + { + if (!strcmp(s->name, seat)) return s; + } + + if (!(s = calloc(1, sizeof(Ecore_Drm_Seat)))) + return NULL; + + s->input = input; + s->name = eina_stringshare_add(seat); + + input->dev->seats = eina_list_append(input->dev->seats, s); + + return s; +} + +static Eina_Bool +_device_add(Ecore_Drm_Input *input, struct udev_device *device) +{ + Ecore_Drm_Evdev *edev; + Ecore_Drm_Seat *seat; + const char *dev_seat, *wl_seat; + const char *node; + char n[PATH_MAX]; + int fd = -1; + + if (!(dev_seat = udev_device_get_property_value(device, "ID_SEAT"))) + dev_seat = "seat0"; + + if (strcmp(dev_seat, input->seat)) return EINA_FALSE; + + if (!(wl_seat = udev_device_get_property_value(device, "WL_SEAT"))) + wl_seat = "seat0"; + + if (!(seat = _seat_get(input, wl_seat))) + return EINA_FALSE; + + node = udev_device_get_devnode(device); + strcpy(n, node); + fd = open(n, O_RDWR | O_NONBLOCK); + /* _ecore_drm_message_send(ECORE_DRM_OP_DEVICE_OPEN, -1, n, strlen(n)); */ + /* _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_OPEN, &fd, NULL, 0); */ + /* DBG("Opened Restricted Input: %s %d", node, fd); */ + + if (!(edev = _ecore_drm_evdev_device_create(seat, node, fd))) + { + close(fd); + /* _ecore_drm_message_send(ECORE_DRM_OP_DEVICE_CLOSE, fd, NULL, 0); */ + /* _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_OPEN, &fd, NULL, 0); */ + return EINA_FALSE; + } + + seat->devices = eina_list_append(seat->devices, edev); + + /* TODO: finish */ + + return EINA_TRUE; +} + +static void +_device_remove(Ecore_Drm_Input *input, const char *device) +{ + Ecore_Drm_Seat *seat; + Eina_List *l; + + if (!input) return; + + EINA_LIST_FOREACH(input->dev->seats, l, seat) + { + Ecore_Drm_Evdev *edev; + Eina_List *ll; + + EINA_LIST_FOREACH(seat->devices, ll, edev) + { + if (!strcmp(edev->path, device)) + { + seat->devices = eina_list_remove(seat->devices, edev); + _ecore_drm_evdev_device_destroy(edev); + break; + } + } + } +} + +static Eina_Bool +_cb_input_event(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED) +{ + Ecore_Drm_Input *input; + struct udev_device *udevice; + const char *act; + + DBG("Input Event"); + + if (!(input = data)) return EINA_FALSE; + + if (!(udevice = udev_monitor_receive_device(input->monitor))) + return EINA_TRUE; + + if (!(act = udev_device_get_action(udevice))) return EINA_TRUE; + + if (strncmp("event", udev_device_get_sysname(udevice), 5) != 0) + goto err; + + if (!strcmp(act, "add")) + { + DBG("\tDevice Added"); + _device_add(input, udevice); + } + else if (!strcmp(act, "remove")) + { + const char *node; + + node = udev_device_get_devnode(udevice); + + DBG("\tDevice Removed: %s", node); + _device_remove(input, node); + } + + return EINA_TRUE; + +err: + if (udevice) udev_device_unref(udevice); + return EINA_TRUE; +} + +static Eina_Bool +_devices_add(Ecore_Drm_Input *input) +{ + struct udev_enumerate *uenum; + struct udev_list_entry *uentry; + struct udev_device *udevice; + const char *path, *name; + Eina_Bool found = EINA_FALSE; + + uenum = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(uenum, "input"); + udev_enumerate_scan_devices(uenum); + + udev_list_entry_foreach(uentry, udev_enumerate_get_list_entry(uenum)) + { + path = udev_list_entry_get_name(uentry); + udevice = udev_device_new_from_syspath(udev, path); + name = udev_device_get_sysname(udevice); + + if (strncmp("event", name, 5) != 0) + { + udev_device_unref(udevice); + continue; + } + + if (!_device_add(input, udevice)) + { + udev_device_unref(udevice); + continue; + } + + found = EINA_TRUE; + + udev_device_unref(udevice); + } + + udev_enumerate_unref(uenum); + + if (!found) + { + ERR("No Input Devices Found"); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +/* public functions */ +EAPI Eina_Bool +ecore_drm_inputs_create(Ecore_Drm_Device *dev) +{ + Ecore_Drm_Input *input; + + /* check for valid device */ + if ((!dev) || (!udev)) return EINA_FALSE; + + /* try to allocate space for input structure */ + if (!(input = calloc(1, sizeof(Ecore_Drm_Input)))) + return EINA_FALSE; + + /* FIXME: Hardcoded seat name */ + input->seat = eina_stringshare_add("seat0"); + input->dev = dev; + + /* try to enable this input */ + if (!ecore_drm_inputs_enable(input)) + { + ERR("Could not enable input"); + if (input->seat) eina_stringshare_del(input->seat); + free(input); + return EINA_FALSE; + } + + /* add this input to dev */ + dev->inputs = eina_list_append(dev->inputs, input); + + return EINA_TRUE; +} + +EAPI void +ecore_drm_inputs_destroy(Ecore_Drm_Device *dev) +{ + Ecore_Drm_Seat *seat; + Eina_List *l; + + if (!dev) return; + + EINA_LIST_FOREACH(dev->seats, l, seat) + { + Ecore_Drm_Evdev *edev; + + EINA_LIST_FREE(seat->devices, edev) + _ecore_drm_evdev_device_destroy(edev); + } +} + +EAPI Eina_Bool +ecore_drm_inputs_enable(Ecore_Drm_Input *input) +{ + /* check for valid input */ + if (!input) return EINA_FALSE; + + if (!input->monitor) + input->monitor = udev_monitor_new_from_netlink(udev, "udev"); + + if (!input->monitor) + { + ERR("Could not create udev monitor: %m"); + return EINA_FALSE; + } + + /* setup input filter */ + udev_monitor_filter_add_match_subsystem_devtype(input->monitor, + "input", NULL); + + /* try to enable receiving udev events */ + if (udev_monitor_enable_receiving(input->monitor)) + { + ERR("Could not bind udev monitor: %m"); + udev_monitor_unref(input->monitor); + return EINA_FALSE; + } + + /* save the fd */ + if ((input->fd = udev_monitor_get_fd(input->monitor)) < 0) + { + ERR("Input monitor has no fd: %m"); + udev_monitor_unref(input->monitor); + return EINA_FALSE; + } + + /* create fd handler */ + if (!input->hdlr) + { + input->hdlr = + ecore_main_fd_handler_add(input->fd, ECORE_FD_READ, + _cb_input_event, input, NULL, NULL); + } + + if (!input->hdlr) + { + ERR("Failed to setup input fd handler: %m"); + udev_monitor_unref(input->monitor); + return EINA_FALSE; + } + + /* try to add devices */ + if (!_devices_add(input)) + { + ERR("Could not add input devices"); + udev_monitor_unref(input->monitor); + return EINA_FALSE; + } + + input->enabled = EINA_TRUE; + input->suspended = EINA_FALSE; + + return EINA_TRUE; +} + +EAPI void +ecore_drm_inputs_disable(Ecore_Drm_Input *input) +{ + if (!input) return; +} diff --git a/src/lib/ecore_drm/ecore_drm_output.c b/src/lib/ecore_drm/ecore_drm_output.c new file mode 100644 index 0000000000..f55e1f1f34 --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm_output.c @@ -0,0 +1,703 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "ecore_drm_private.h" + +#define ALEN(array) (sizeof(array) / sizeof(array)[0]) + +static const char *conn_types[] = +{ + "None", "VGA", "DVI", "DVI", "DVI", + "Composite", "TV", "LVDS", "CTV", "DIN", + "DP", "HDMI", "HDMI", "TV", "eDP", +}; + +/* local functions */ +/* #ifdef HAVE_GBM */ +/* static Eina_Bool */ +/* _ecore_drm_output_context_create(Ecore_Drm_Device *dev, EGLSurface surface) */ +/* { */ +/* EGLBoolean r; */ +/* static const EGLint attribs[] = */ +/* { */ +/* EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE */ +/* }; */ + +/* if ((!dev->egl.disp) || (!dev->egl.cfg)) return EINA_FALSE; */ + +/* if (!eglBindAPI(EGL_OPENGL_ES_API)) */ +/* { */ +/* ERR("Could not bind egl api"); */ +/* return EINA_FALSE; */ +/* } */ + +/* dev->egl.ctxt = */ +/* eglCreateContext(dev->egl.disp, dev->egl.cfg, EGL_NO_CONTEXT, attribs); */ +/* if (!dev->egl.ctxt) */ +/* { */ +/* ERR("Could not create Egl Context"); */ +/* return EINA_FALSE; */ +/* } */ + +/* r = eglMakeCurrent(dev->egl.disp, surface, surface, dev->egl.ctxt); */ +/* if (r == EGL_FALSE) */ +/* { */ +/* ERR("Could not make surface current"); */ +/* return EINA_FALSE; */ +/* } */ + +/* return EINA_TRUE; */ +/* } */ + +/* static Eina_Bool */ +/* _ecore_drm_output_hardware_setup(Ecore_Drm_Device *dev, Ecore_Drm_Output *output) */ +/* { */ +/* unsigned int i = 0; */ +/* int flags = 0; */ +/* int w = 0, h = 0; */ + +/* if ((!dev) || (!output)) return EINA_FALSE; */ + +/* if (output->current_mode) */ +/* { */ +/* w = output->current_mode->width; */ +/* h = output->current_mode->height; */ +/* } */ +/* else */ +/* { */ +/* w = 1024; */ +/* h = 768; */ +/* } */ + +/* flags = (GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); */ + +/* if (!(output->surface = */ +/* gbm_surface_create(dev->gbm, w, h, GBM_FORMAT_ARGB8888, flags))) */ +/* { */ +/* ERR("Could not create output surface"); */ +/* return EINA_FALSE; */ +/* } */ + +/* if (!(output->egl.surface = */ +/* eglCreateWindowSurface(dev->egl.disp, dev->egl.cfg, */ +/* output->surface, NULL))) */ +/* { */ +/* ERR("Could not create output egl surface"); */ +/* gbm_surface_destroy(output->surface); */ +/* return EINA_FALSE; */ +/* } */ + +/* if (!dev->egl.ctxt) */ +/* { */ +/* if (!_ecore_drm_output_context_create(dev, output->egl.surface)) */ +/* { */ +/* ERR("Could not create context"); */ +/* gbm_surface_destroy(output->surface); */ +/* return EINA_FALSE; */ +/* } */ +/* } */ + +/* flags = (GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE); */ +/* for (i = 0; i < NUM_FRAME_BUFFERS; i++) */ +/* { */ +/* if (output->cursor[i]) continue; */ +/* if (!(output->cursor[i] = */ +/* gbm_bo_create(dev->gbm, 64, 64, dev->format, flags))) */ +/* { */ +/* continue; */ +/* } */ +/* } */ + +/* if ((!output->cursor[0]) || (!output->cursor[1])) */ +/* { */ +/* WRN("Hardware Cursor Buffers not available"); */ +/* dev->cursors_broken = EINA_TRUE; */ +/* } */ + +/* return EINA_TRUE; */ +/* } */ + +/* static void */ +/* _ecore_drm_output_hardware_render(Ecore_Drm_Output *output) */ +/* { */ +/* struct gbm_bo *bo; */ +/* int ret; */ + +/* if (!output) return; */ +/* if (!output->current_mode) return; */ + +/* glViewport(output->x, output->y, */ +/* output->current_mode->width, output->current_mode->height); */ + +/* if (eglMakeCurrent(output->dev->egl.disp, output->egl.surface, */ +/* output->egl.surface, output->dev->egl.ctxt) == EGL_FALSE) */ +/* return; */ + +/* glClearColor(1.0, 1.0, 0.0, 1.0); */ +/* glClear(GL_COLOR_BUFFER_BIT); */ +/* glFlush(); */ + +/* eglSwapBuffers(output->dev->egl.disp, output->egl.surface); */ + +/* if (!(bo = gbm_surface_lock_front_buffer(output->surface))) */ +/* { */ +/* ERR("Failed to lock front buffer"); */ +/* return; */ +/* } */ + +/* if (!(output->next = _ecore_drm_fb_bo_get(output->dev, bo))) */ +/* { */ +/* ERR("Failed to get FB from bo"); */ +/* gbm_surface_release_buffer(output->surface, bo); */ +/* } */ +/* } */ +/* #endif */ + +static Eina_Bool +_ecore_drm_output_software_setup(Ecore_Drm_Device *dev, Ecore_Drm_Output *output) +{ + unsigned int i = 0; + int w = 0, h = 0; + + if ((!dev) || (!output)) return EINA_FALSE; + + if (output->current_mode) + { + w = output->current_mode->width; + h = output->current_mode->height; + } + else + { + w = 1024; + h = 768; + } + + for (i = 0; i < NUM_FRAME_BUFFERS; i++) + { + if (!(output->dumb[i] = ecore_drm_fb_create(dev, w, h))) + { + ERR("Could not create dumb framebuffer %d", i); + goto err; + } + } + + return EINA_TRUE; + +err: + for (i = 0; i < NUM_FRAME_BUFFERS; i++) + { + if (output->dumb[i]) ecore_drm_fb_destroy(output->dumb[i]); + output->dumb[i] = NULL; + } + + return EINA_FALSE; +} + +static void +_ecore_drm_output_software_render(Ecore_Drm_Output *output) +{ + if (!output) return; + if (!output->current_mode) return; +} + +static int +_ecore_drm_output_crtc_find(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnector *conn) +{ + drmModeEncoder *enc; + unsigned int p; + int i = 0, j = 0; + + for (j = 0; j < conn->count_encoders; j++) + { + /* get the encoder on this connector */ + if (!(enc = drmModeGetEncoder(dev->drm.fd, conn->encoders[j]))) + { + ERR("Failed to get encoder: %m"); + return -1; + } + + p = enc->possible_crtcs; + drmModeFreeEncoder(enc); + + for (i = 0; i < res->count_crtcs; i++) + { + if ((p & (1 << i)) && + (!(dev->crtc_allocator & (1 << res->crtcs[i])))) + { + return i; + } + } + } + + return -1; +} + +static Ecore_Drm_Output_Mode * +_ecore_drm_output_mode_add(Ecore_Drm_Output *output, drmModeModeInfo *info) +{ + Ecore_Drm_Output_Mode *mode; + uint64_t refresh; + + /* try to allocate space for mode */ + if (!(mode = malloc(sizeof(Ecore_Drm_Output_Mode)))) + { + ERR("Could not allocate space for mode"); + return NULL; + } + + mode->flags = 0; + mode->width = info->hdisplay; + mode->height = info->vdisplay; + + refresh = (info->clock * 1000000LL / info->htotal + info->vtotal / 2) / info->vtotal; + if (info->flags & DRM_MODE_FLAG_INTERLACE) + refresh *= 2; + if (info->flags & DRM_MODE_FLAG_DBLSCAN) + refresh /= 2; + if (info->vscan > 1) + refresh /= info->vscan; + + mode->refresh = refresh; + mode->info = *info; + + /* DBG("Added Mode: %dx%d@%d to Output %d", */ + /* mode->width, mode->height, mode->refresh, output->crtc_id); */ + + output->modes = eina_list_append(output->modes, mode); + + return mode; +} + +static Ecore_Drm_Output * +_ecore_drm_output_create(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnector *conn, int x, int y) +{ + Ecore_Drm_Output *output; + Ecore_Drm_Output_Mode *mode; + const char *conn_name; + char name[32]; + int i = 0; + drmModeEncoder *enc; + drmModeModeInfo crtc_mode; + drmModeCrtc *crtc; + Eina_List *l; + + i = _ecore_drm_output_crtc_find(dev, res, conn); + if (i < 0) + { + ERR("Could not find crtc or encoder for connector"); + return NULL; + } + + /* try to allocate space for output */ + if (!(output = calloc(1, sizeof(Ecore_Drm_Output)))) + { + ERR("Could not allocate space for output"); + return NULL; + } + + output->dev = dev; + output->x = x; + output->y = y; + + output->subpixel = conn->subpixel; + output->make = eina_stringshare_add("unknown"); + output->model = eina_stringshare_add("unknown"); + + if (conn->connector_type < ALEN(conn_types)) + conn_name = conn_types[conn->connector_type]; + else + conn_name = "UNKNOWN"; + + snprintf(name, sizeof(name), "%s%d", conn_name, conn->connector_type_id); + output->name = eina_stringshare_add(name); + + output->crtc_id = res->crtcs[i]; + dev->crtc_allocator |= (1 << output->crtc_id); + output->conn_id = conn->connector_id; + output->crtc = drmModeGetCrtc(dev->drm.fd, output->crtc_id); + + memset(&mode, 0, sizeof(mode)); + if ((enc = drmModeGetEncoder(dev->drm.fd, conn->encoder_id))) + { + crtc = drmModeGetCrtc(dev->drm.fd, enc->crtc_id); + drmModeFreeEncoder(enc); + if (!crtc) goto mode_err; + if (crtc->mode_valid) crtc_mode = crtc->mode; + drmModeFreeCrtc(crtc); + } + + for (i = 0; i < conn->count_modes; i++) + { + if (!(mode = _ecore_drm_output_mode_add(output, &conn->modes[i]))) + { + ERR("Failed to add mode to output"); + goto mode_err; + } + } + + EINA_LIST_REVERSE_FOREACH(output->modes, l, mode) + { + if (!memcmp(&crtc_mode, &mode->info, sizeof(crtc_mode))) + { + output->current_mode = mode; + break; + } + } + + if (!output->current_mode) + output->current_mode = _ecore_drm_output_mode_add(output, &crtc_mode); + +/* #ifdef HAVE_GBM */ +/* if ((dev->use_hw_accel) && (dev->gbm)) */ +/* { */ +/* if (!_ecore_drm_output_hardware_setup(dev, output)) */ +/* { */ +/* ERR("Could not setup output for hardware acceleration"); */ +/* dev->use_hw_accel = EINA_FALSE; */ +/* if (!_ecore_drm_output_software_setup(dev, output)) */ +/* goto mode_err; */ +/* else */ +/* DBG("Setup Output %d for Software Rendering", output->crtc_id); */ +/* } */ +/* else */ +/* DBG("Setup Output %d for Hardware Acceleration", output->crtc_id); */ +/* } */ +/* else */ +/* #endif */ + { + dev->use_hw_accel = EINA_FALSE; + if (!_ecore_drm_output_software_setup(dev, output)) + goto mode_err; + else + DBG("Setup Output %d for Software Rendering", output->crtc_id); + } + + /* TODO */ + + return output; + +mode_err: + EINA_LIST_FREE(output->modes, mode) + free(mode); + drmModeFreeCrtc(output->crtc); + dev->crtc_allocator &= ~(1 << output->crtc_id); + free(output); + return NULL; +} + +void +_ecore_drm_output_frame_finish(Ecore_Drm_Output *output) +{ + if (!output) return; + + if (output->need_repaint) ecore_drm_output_repaint(output); + + output->repaint_scheduled = EINA_FALSE; +} + +void +_ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb) +{ + if ((!output) || (!fb)) return; + + if ((fb->mmap) && (fb != output->dumb[0]) && (fb != output->dumb[1])) + ecore_drm_fb_destroy(fb); +/* #ifdef HAVE_GBM */ +/* else if (fb->bo) */ +/* gbm_bo_destroy(fb->bo); */ +/* #endif */ +} + +void +_ecore_drm_output_repaint_start(Ecore_Drm_Output *output) +{ + unsigned int fb; + + DBG("Output Repaint Start"); + + if (!output) return; + + if (!output->current) + { + DBG("\tNo Current FB"); + goto finish; + } + + fb = output->current->id; + + if (drmModePageFlip(output->dev->drm.fd, output->crtc_id, fb, + DRM_MODE_PAGE_FLIP_EVENT, output) < 0) + { + ERR("Could not schedule output page flip event"); + goto finish; + } + + return; + +finish: + _ecore_drm_output_frame_finish(output); +} + +/** + * @defgroup Ecore_Drm_Output_Group + * + */ + +/* TODO: DOXY !! */ + +/* public functions */ +EAPI Eina_Bool +ecore_drm_outputs_create(Ecore_Drm_Device *dev) +{ + Eina_Bool ret = EINA_TRUE; + Ecore_Drm_Output *output; + drmModeConnector *conn; + drmModeRes *res; + drmModeCrtc *crtc; + int i = 0, x = 0, y = 0; + + /* DBG("Create outputs for %d", dev->drm.fd); */ + + /* get the resources */ + if (!(res = drmModeGetResources(dev->drm.fd))) + { + ERR("Could not get resources for drm card: %m"); + return EINA_FALSE; + } + + if (!(dev->crtcs = calloc(res->count_crtcs, sizeof(unsigned int)))) + { + ERR("Could not allocate space for crtcs"); + /* free resources */ + drmModeFreeResources(res); + return EINA_FALSE; + } + + dev->crtc_count = res->count_crtcs; + memcpy(dev->crtcs, res->crtcs, sizeof(unsigned int) * res->count_crtcs); + + dev->min_width = res->min_width; + dev->min_height = res->min_height; + dev->max_width = res->max_width; + dev->max_height = res->max_height; + + for (i = 0; i < res->count_connectors; i++) + { + if (i > 0) break; + /* get the connector */ + if (!(conn = drmModeGetConnector(dev->drm.fd, res->connectors[i]))) + continue; + + if ((conn->connection == DRM_MODE_CONNECTED) && + (conn->count_modes > 0)) + { + drmModeEncoder *enc; + + /* create output for this connector */ + if (!(output = _ecore_drm_output_create(dev, res, conn, x, y))) + { + /* free the connector */ + drmModeFreeConnector(conn); + continue; + } + + output->drm_fd = dev->drm.fd; + dev->outputs = eina_list_append(dev->outputs, output); + + if (!(enc = drmModeGetEncoder(dev->drm.fd, conn->encoder_id))) + { + drmModeFreeConnector(conn); + continue; + } + + if (!(crtc = drmModeGetCrtc(dev->drm.fd, enc->crtc_id))) + { + drmModeFreeEncoder(enc); + drmModeFreeConnector(conn); + continue; + } + + x += crtc->width; + + drmModeFreeCrtc(crtc); + drmModeFreeEncoder(enc); + } + + /* free the connector */ + drmModeFreeConnector(conn); + } + + ret = EINA_TRUE; + if (eina_list_count(dev->outputs) < 1) + { + ret = EINA_FALSE; + free(dev->crtcs); + } + + /* free resources */ + drmModeFreeResources(res); + + /* TODO: add hook for udev drm output updates */ + + return ret; +} + +EAPI void +ecore_drm_output_free(Ecore_Drm_Output *output) +{ + Ecore_Drm_Output_Mode *mode; + + /* check for valid output */ + if (!output) return; + + /* free modes */ + EINA_LIST_FREE(output->modes, mode) + free(mode); + + /* free strings */ + if (output->name) eina_stringshare_del(output->name); + if (output->model) eina_stringshare_del(output->model); + if (output->make) eina_stringshare_del(output->make); + + free(output); +} + +EAPI void +ecore_drm_output_cursor_size_set(Ecore_Drm_Output *output, int handle, int w, int h) +{ + if (!output) return; + drmModeSetCursor(output->drm_fd, output->crtc_id, handle, w, h); +} + +EAPI Eina_Bool +ecore_drm_output_enable(Ecore_Drm_Output *output) +{ + Ecore_Drm_Output_Mode *mode; + + if ((!output) || (!output->current)) return EINA_FALSE; + + mode = output->current_mode; + if (drmModeSetCrtc(output->drm_fd, output->crtc_id, output->current->id, + 0, 0, &output->conn_id, 1, &mode->info) < 0) + { + ERR("Could not set output crtc: %m"); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +EAPI void +ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb) +{ + if ((!output) || (!fb)) return; + + if ((fb->mmap) && (fb != output->dumb[0]) && (fb != output->dumb[1])) + ecore_drm_fb_destroy(fb); +/* #ifdef HAVE_GBM */ +/* else if (fb->bo) */ +/* { */ +/* if (fb->from_client) */ +/* gbm_bo_destroy(fb->bo); */ +/* else */ +/* gbm_surface_release_buffer(output->surface, fb->bo); */ +/* } */ +/* #endif */ +} + +EAPI void +ecore_drm_output_repaint(Ecore_Drm_Output *output) +{ + Eina_List *l; + Ecore_Drm_Sprite *sprite; + int ret = 0; + + if (!output) return; + + DBG("Output Repaint: %d %d", output->crtc_id, output->conn_id); + + /* TODO: assign planes ? */ + + if (!output->next) + { +/* #ifdef HAVE_GBM */ +/* if (output->dev->use_hw_accel) */ +/* { */ +/* _ecore_drm_output_hardware_render(output); */ +/* } */ +/* else */ +/* #endif */ + { + _ecore_drm_output_software_render(output); + } + } + + if (!output->next) + { + DBG("\tNo Next Fb"); + return; + } + + output->need_repaint = EINA_FALSE; + + if (!output->current) + { + Ecore_Drm_Output_Mode *mode; + + mode = output->current_mode; + + ret = drmModeSetCrtc(output->dev->drm.fd, output->crtc_id, + output->next->id, 0, 0, &output->conn_id, 1, + &mode->info); + if (ret) + { + ERR("Setting output mode failed"); + goto err; + } + } + + if (drmModePageFlip(output->dev->drm.fd, output->crtc_id, output->next->id, + DRM_MODE_PAGE_FLIP_EVENT, output) < 0) + { + ERR("Scheduling pageflip failed"); + goto err; + } + + output->pending_flip = EINA_TRUE; + + EINA_LIST_FOREACH(output->dev->sprites, l, sprite) + { + unsigned int flags = 0, id = 0; + drmVBlank vbl = + { + .request.type = (DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT), + .request.sequence = 1, + }; + + if (((!sprite->current_fb) && (!sprite->next_fb)) || + (!ecore_drm_sprites_crtc_supported(output, sprite->crtcs))) + continue; + + if ((sprite->next_fb) && (!output->dev->cursors_broken)) + id = sprite->next_fb->id; + + ecore_drm_sprites_fb_set(sprite, id, flags); + + vbl.request.signal = (unsigned long)sprite; + ret = drmWaitVBlank(output->dev->drm.fd, &vbl); + if (ret) ERR("Error Wait VBlank: %m"); + + sprite->output = output; + output->pending_vblank = EINA_TRUE; + } + + return; + +err: + if (output->next) + { + ecore_drm_output_fb_release(output, output->next); + output->next = NULL; + } +} diff --git a/src/lib/ecore_drm/ecore_drm_private.h b/src/lib/ecore_drm/ecore_drm_private.h new file mode 100644 index 0000000000..51cd78283b --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm_private.h @@ -0,0 +1,281 @@ +#ifndef _ECORE_DRM_PRIVATE_H +# define _ECORE_DRM_PRIVATE_H + +# include "Ecore.h" +# include "ecore_private.h" +# include "Ecore_Input.h" + +# include +# include +# include +# include +# include +# include +# include + +# include +# include +//# include + +# include +# include +# include + +/* # ifdef HAVE_GBM */ +/* # include */ +/* # include */ +/* # include */ +/* # include */ +/* # include */ +/* # endif */ + +# include + +# ifndef DRM_MAJOR +# define DRM_MAJOR 226 +# endif + +# ifndef DRM_CAP_TIMESTAMP_MONOTONIC +# define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 +# endif + +# ifdef ECORE_DRM_DEFAULT_LOG_COLOR +# undef ECORE_DRM_DEFAULT_LOG_COLOR +# endif +# define ECORE_DRM_DEFAULT_LOG_COLOR EINA_COLOR_BLUE + +# define EVDEV_SEAT_POINTER (1 << 0) +# define EVDEV_SEAT_KEYBOARD (1 << 1) +# define EVDEV_SEAT_TOUCH (1 << 2) + +# ifdef ERR +# undef ERR +# endif +# ifdef DBG +# undef DBG +# endif +# ifdef INF +# undef INF +# endif +# ifdef WRN +# undef WRN +# endif +# ifdef CRIT +# undef CRIT +# endif + +extern int _ecore_drm_log_dom; + +/* undef this for non-testing builds */ +# define LOG_TO_FILE + +# ifdef LOG_TO_FILE +extern FILE *lg; + +# define ERR(...) \ + EINA_LOG_DOM_ERR(_ecore_drm_log_dom, __VA_ARGS__); \ + fflush(lg); + +# define DBG(...) \ + EINA_LOG_DOM_DBG(_ecore_drm_log_dom, __VA_ARGS__); \ + fflush(lg); + +# define INF(...) \ + EINA_LOG_DOM_INFO(_ecore_drm_log_dom, __VA_ARGS__); \ + fflush(lg); + +# define WRN(...) \ + EINA_LOG_DOM_WARN(_ecore_drm_log_dom, __VA_ARGS__); \ + fflush(lg); + +# define CRIT(...) \ + EINA_LOG_DOM_CRIT(_ecore_drm_log_dom, __VA_ARGS__); \ + fflush(lg); + +# else +# define ERR(...) EINA_LOG_DOM_ERR(_ecore_drm_log_dom, __VA_ARGS__) +# define DBG(...) EINA_LOG_DOM_DBG(_ecore_drm_log_dom, __VA_ARGS__) +# define INF(...) EINA_LOG_DOM_INFO(_ecore_drm_log_dom, __VA_ARGS__) +# define WRN(...) EINA_LOG_DOM_WARN(_ecore_drm_log_dom, __VA_ARGS__) +# define CRIT(...) EINA_LOG_DOM_CRIT(_ecore_drm_log_dom, __VA_ARGS__) +# endif + +extern struct udev *udev; + +struct _Ecore_Drm_Output_Mode +{ + unsigned int flags; + int width, height; + unsigned int refresh; + drmModeModeInfo info; +}; + +struct _Ecore_Drm_Output +{ + Ecore_Drm_Device *dev; + unsigned int crtc_id; + unsigned int conn_id; + drmModeCrtcPtr crtc; + + int x, y; + int drm_fd; + + Eina_Bool need_repaint : 1; + Eina_Bool repaint_scheduled : 1; + + Eina_Bool pending_flip : 1; + Eina_Bool pending_vblank : 1; + + const char *make, *model, *name; + unsigned int subpixel; + + Ecore_Drm_Output_Mode *current_mode; + Eina_List *modes; + + Ecore_Drm_Fb *current, *next; + Ecore_Drm_Fb *dumb[NUM_FRAME_BUFFERS]; + +/* # ifdef HAVE_GBM */ +/* struct gbm_surface *surface; */ +/* struct gbm_bo *cursor[NUM_FRAME_BUFFERS]; */ +/* struct */ +/* { */ +/* EGLSurface surface; */ +/* } egl; */ +/* # endif */ + + /* TODO: finish */ +}; + +struct _Ecore_Drm_Seat +{ +// struct libinput_seat *seat; + const char *name; + Ecore_Drm_Input *input; + Eina_List *devices; +}; + +struct _Ecore_Drm_Input +{ + int fd; + const char *seat; + struct udev_monitor *monitor; + Ecore_Fd_Handler *hdlr; + Ecore_Drm_Device *dev; + + Eina_Bool enabled : 1; + Eina_Bool suspended : 1; +}; + +struct _Ecore_Drm_Evdev +{ + Ecore_Drm_Seat *seat; + /* struct libinput *linput; */ + /* struct libinput_device *dev; */ + const char *name, *path; + int fd; + + struct + { + int min_x, min_y; + int max_x, max_y; + int x, y; + } abs; + + Ecore_Drm_Evdev_Event_Type pending_event; + Ecore_Drm_Evdev_Capabilities caps; + Ecore_Drm_Seat_Capabilities seat_caps; + + void (*event_process)(Ecore_Drm_Evdev *dev, struct input_event *event, int count); + + Ecore_Fd_Handler *hdlr; +}; + +struct _Ecore_Drm_Sprite +{ + Ecore_Drm_Fb *current_fb, *next_fb; + Ecore_Drm_Output *output; + + int drm_fd; + + unsigned int crtcs; + unsigned int plane_id; + + struct + { + int x, y; + unsigned int w, h; + } src, dest; + + unsigned int num_formats; + unsigned int formats[]; +}; + +struct _Ecore_Drm_Device +{ + int id; + const char *seat; + + struct + { + int fd; + const char *name; + const char *path; + clockid_t clock; + Ecore_Fd_Handler *hdlr; + Ecore_Idle_Enterer *idler; + } drm; + + unsigned int min_width, min_height; + unsigned int max_width, max_height; + + unsigned int crtc_count; + unsigned int *crtcs; + unsigned int crtc_allocator; + + Eina_List *seats; + Eina_List *inputs; + Eina_List *outputs; + Eina_List *sprites; + + struct + { + int fd; + const char *name; + Ecore_Event_Handler *event_hdlr; + } tty; + + unsigned int format; + Eina_Bool use_hw_accel : 1; + Eina_Bool cursors_broken : 1; + +/* #ifdef HAVE_GBM */ +/* struct gbm_device *gbm; */ +/* struct */ +/* { */ +/* EGLDisplay disp; */ +/* EGLContext ctxt; */ +/* EGLConfig cfg; */ +/* } egl; */ +/* #endif */ +}; + +void _ecore_drm_message_send(int opcode, int fd, void *data, size_t bytes); +Eina_Bool _ecore_drm_message_receive(int opcode, int *fd, void **data, size_t bytes); + +Ecore_Drm_Evdev *_ecore_drm_evdev_device_create(Ecore_Drm_Seat *seat, const char *path, int fd); +void _ecore_drm_evdev_device_destroy(Ecore_Drm_Evdev *evdev); +/* int _ecore_drm_evdev_event_process(struct libinput_event *event); */ + +Ecore_Drm_Fb *_ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height); +void _ecore_drm_fb_destroy(Ecore_Drm_Fb *fb); + +/* #ifdef HAVE_GBM */ +/* Ecore_Drm_Fb *_ecore_drm_fb_bo_get(Ecore_Drm_Device *dev, struct gbm_bo *bo); */ +/* #endif */ + +void _ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb); +void _ecore_drm_output_repaint_start(Ecore_Drm_Output *output); +void _ecore_drm_output_frame_finish(Ecore_Drm_Output *output); + +#endif diff --git a/src/lib/ecore_drm/ecore_drm_sprites.c b/src/lib/ecore_drm/ecore_drm_sprites.c new file mode 100644 index 0000000000..b457afe018 --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm_sprites.c @@ -0,0 +1,115 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "ecore_drm_private.h" + +/** + * @defgroup Ecore_Drm_Fb_Group + * + */ + +/* TODO: DOXY !! */ + +EAPI Eina_Bool +ecore_drm_sprites_create(Ecore_Drm_Device *dev) +{ + drmModePlaneRes *res; + drmModePlane *p; + unsigned int i = 0; + + /* check for valid device */ + if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE; + + /* get plane resources */ + if (!(res = drmModeGetPlaneResources(dev->drm.fd))) return EINA_FALSE; + + for (i = 0; i < res->count_planes; i++) + { + Ecore_Drm_Sprite *sprite; + + if (!(p = drmModeGetPlane(dev->drm.fd, res->planes[i]))) + continue; + + /* allocate space for sprite */ + if (!(sprite = + malloc(sizeof(Ecore_Drm_Sprite) + + ((sizeof(unsigned int)) * p->count_formats)))) + { + drmModeFreePlane(p); + continue; + } + + sprite->drm_fd = dev->drm.fd; + sprite->crtcs = p->possible_crtcs; + sprite->plane_id = p->plane_id; + sprite->num_formats = p->count_formats; + memcpy(sprite->formats, p->formats, + p->count_formats * sizeof(p->formats[0])); + drmModeFreePlane(p); + + dev->sprites = eina_list_append(dev->sprites, sprite); + } + + /* free resources */ + drmModeFreePlaneResources(res); + + return EINA_TRUE; +} + +EAPI void +ecore_drm_sprites_destroy(Ecore_Drm_Device *dev) +{ + Ecore_Drm_Sprite *sprite; + + /* check for valid device */ + if (!dev) return; + + EINA_LIST_FREE(dev->sprites, sprite) + { + ecore_drm_sprites_fb_set(sprite, 0, 0); + + _ecore_drm_output_fb_release(sprite->output, sprite->current_fb); + _ecore_drm_output_fb_release(sprite->output, sprite->next_fb); + + free(sprite); + } +} + +EAPI void +ecore_drm_sprites_fb_set(Ecore_Drm_Sprite *sprite, int fb_id, int flags) +{ + if ((!sprite) || (!sprite->output)) return; + + if (fb_id) + { + drmModeSetPlane(sprite->drm_fd, sprite->plane_id, + sprite->output->crtc_id, fb_id, flags, + sprite->dest.x, sprite->dest.y, sprite->dest.w, + sprite->dest.h, sprite->src.x, sprite->src.y, + sprite->src.w, sprite->src.h); + } + else + { + drmModeSetPlane(sprite->drm_fd, sprite->plane_id, + sprite->output->crtc_id, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + } +} + +EAPI Eina_Bool +ecore_drm_sprites_crtc_supported(Ecore_Drm_Output *output, unsigned int supported) +{ + Ecore_Drm_Device *dev; + unsigned int c = 0; + + dev = output->dev; + + for (c = 0; c < dev->crtc_count; c++) + { + if (dev->crtcs[c] != output->crtc_id) continue; + if ((supported) && (1 << c)) return EINA_FALSE; + } + + return EINA_TRUE; +} diff --git a/src/lib/ecore_drm/ecore_drm_tty.c b/src/lib/ecore_drm/ecore_drm_tty.c new file mode 100644 index 0000000000..41a0561fb7 --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm_tty.c @@ -0,0 +1,297 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "ecore_drm_private.h" +#include +#include +#include +#include + +#ifndef KDSKBMUTE +# define KDSKBMUTE 0x4B51 +#endif + +static Eina_Bool +_ecore_drm_tty_cb_signal(void *data, int type EINA_UNUSED, void *event) +{ + Ecore_Drm_Device *dev; + Ecore_Event_Signal_User *ev; + + dev = data; + ev = event; + + DBG("Caught user signal: %d", ev->number); + + if (ev->number == 1) + { + Ecore_Drm_Input *input; + Ecore_Drm_Output *output; + Ecore_Drm_Sprite *sprite; + Eina_List *l; + + DBG("Release VT"); + + /* disable inputs (suspends) */ + EINA_LIST_FOREACH(dev->inputs, l, input) + ecore_drm_inputs_disable(input); + + /* disable hardware cursor */ + EINA_LIST_FOREACH(dev->outputs, l, output) + ecore_drm_output_cursor_size_set(output, 0, 0, 0); + + /* disable sprites */ + EINA_LIST_FOREACH(dev->sprites, l, sprite) + ecore_drm_sprites_fb_set(sprite, 0, 0); + + /* close input fds ?? */ + + /* drop drm master */ + if (ecore_drm_device_master_drop(dev)) + { + /* issue ioctl to release vt */ + if (!ecore_drm_tty_release(dev)) + ERR("Could not release VT: %m"); + } + else + ERR("Could not drop drm master: %m"); + } + else if (ev->number == 2) + { + Ecore_Drm_Output *output; + Ecore_Drm_Input *input; + Eina_List *l; + + DBG("Acquire VT"); + + /* issue ioctl to acquire vt */ + if (ecore_drm_tty_acquire(dev)) + { + /* set drm master */ + if (!ecore_drm_device_master_set(dev)) + ERR("Could not set drm master: %m"); + + /* set output mode */ + EINA_LIST_FOREACH(dev->outputs, l, output) + ecore_drm_output_enable(output); + + /* enable inputs */ + EINA_LIST_FOREACH(dev->inputs, l, input) + ecore_drm_inputs_enable(input); + } + else + ERR("Could not acquire VT: %m"); + } + + return EINA_TRUE; +} + +static Eina_Bool +_ecore_drm_tty_setup(Ecore_Drm_Device *dev) +{ + struct stat st; + int kb_mode; + struct vt_mode vtmode = { 0 }; + + if (fstat(dev->tty.fd, &st) == -1) + { + ERR("Failed to get stats for tty: %m"); + return EINA_FALSE; + } + + if (ioctl(dev->tty.fd, KDGKBMODE, &kb_mode)) + { + ERR("Could not get tty keyboard mode: %m"); + return EINA_FALSE; + } + + /* NB: Don't set this. This Turns OFF keyboard on the VT */ + /* if (ioctl(dev->tty.fd, KDSKBMUTE, 1) && */ + /* ioctl(dev->tty.fd, KDSKBMODE, K_OFF)) */ + /* { */ + /* ERR("Could not set K_OFF keyboard mode: %m"); */ + /* return EINA_FALSE; */ + /* } */ + + /* if (ioctl(dev->tty.fd, KDSETMODE, KD_GRAPHICS)) */ + /* { */ + /* ERR("Could not set graphics mode: %m"); */ + /* return EINA_FALSE; */ + /* } */ + + vtmode.mode = VT_PROCESS; + vtmode.waitv = 0; + vtmode.relsig = SIGUSR1; + vtmode.acqsig = SIGUSR2; + if (ioctl(dev->tty.fd, VT_SETMODE, &vtmode) < 0) + { + ERR("Could not set Terminal Mode: %m"); + return EINA_FALSE; + } + + /* if (ioctl(dev->tty.fd, VT_ACTIVATE, minor(st.st_rdev)) < 0) */ + /* { */ + /* ERR("Failed to activate vt: %m"); */ + /* return EINA_FALSE; */ + /* } */ + + /* if (ioctl(dev->tty.fd, VT_WAITACTIVE, minor(st.st_rdev)) < 0) */ + /* { */ + /* ERR("Failed to wait active: %m"); */ + /* return EINA_FALSE; */ + /* } */ + + return EINA_TRUE; +} + +/** + * @defgroup Ecore_Drm_Tty_Group Tty manipulation functions + * + * Functions that deal with opening, closing, and otherwise using a tty + */ + +/** + * Open a tty for use + * + * @param dev The Ecore_Drm_Device that this tty will belong to. + * @param name The name of the tty to try and open. + * If NULL, /dev/tty0 will be used. + * + * @return EINA_TRUE on success, EINA_FALSE on failure + * + * @ingroup Ecore_Drm_Tty_Group + */ +EAPI Eina_Bool +ecore_drm_tty_open(Ecore_Drm_Device *dev, const char *name) +{ + char tty[32] = ""; + + /* check for valid device */ + if ((!dev) || (!dev->drm.name)) return EINA_FALSE; + + /* assign default tty fd of -1 */ + dev->tty.fd = -1; + + if (!name) + { + char *env; + + if ((env = getenv("ECORE_DRM_TTY"))) + snprintf(tty, sizeof(tty), "%s", env); + else + dev->tty.fd = STDIN_FILENO; + } + else // FIXME: NB: This should Really check for format of name (/dev/xyz) + snprintf(tty, sizeof(tty), "%s", name); + + if (dev->tty.fd < 0) + { + DBG("Trying to Open Tty: %s", tty); + + dev->tty.fd = open(tty, O_RDWR | O_NOCTTY); + if (dev->tty.fd < 0) + { + DBG("Failed to Open Tty: %m"); + return EINA_FALSE; + } + } + + if (dev->tty.fd < 0) + { + DBG("Failed to open tty %s", tty); + return EINA_FALSE; + } + + DBG("Opened Tty %s : %d", tty, dev->tty.fd); + + /* save tty name */ + dev->tty.name = eina_stringshare_add(tty); + + /* FIXME */ + if (!_ecore_drm_tty_setup(dev)) + return EINA_FALSE; + + /* setup handler for signals */ + dev->tty.event_hdlr = + ecore_event_handler_add(ECORE_EVENT_SIGNAL_USER, + _ecore_drm_tty_cb_signal, dev); + + /* set current tty into env */ + setenv("ECORE_DRM_TTY", tty, 1); + + return EINA_TRUE; +} + +/** + * Close an already opened tty + * + * @param dev The Ecore_Drm_Device which owns this tty. + * + * @return EINA_TRUE on success, EINA_FALSE on failure + * + * @ingroup Ecore_Drm_Tty_Group + */ +EAPI Eina_Bool +ecore_drm_tty_close(Ecore_Drm_Device *dev) +{ + /* check for valid device */ + if ((!dev) || (!dev->drm.name)) return EINA_FALSE; + + close(dev->tty.fd); + + dev->tty.fd = -1; + + /* destroy the event handler */ + if (dev->tty.event_hdlr) ecore_event_handler_del(dev->tty.event_hdlr); + dev->tty.event_hdlr = NULL; + + /* clear the tty name */ + if (dev->tty.name) eina_stringshare_del(dev->tty.name); + dev->tty.name = NULL; + + unsetenv("ECORE_DRM_TTY"); + + return EINA_TRUE; +} + +/** + * Release a virtual terminal + * + * @param dev The Ecore_Drm_Device which owns this tty. + * + * @return EINA_TRUE on success, EINA_FALSE on failure + * + * @ingroup Ecore_Drm_Tty_Group + */ +EAPI Eina_Bool +ecore_drm_tty_release(Ecore_Drm_Device *dev) +{ + /* check for valid device */ + if ((!dev) || (!dev->drm.name) || (dev->tty.fd < 0)) return EINA_FALSE; + + /* send ioctl for vt release */ + if (ioctl(dev->tty.fd, VT_RELDISP, 1) < 0) return EINA_FALSE; + + return EINA_TRUE; +} + +/** + * Acquire a virtual terminal + * + * @param dev The Ecore_Drm_Device which owns this tty. + * + * @return EINA_TRUE on success, EINA_FALSE on failure + * + * @ingroup Ecore_Drm_Tty_Group + */ +EAPI Eina_Bool +ecore_drm_tty_acquire(Ecore_Drm_Device *dev) +{ + /* check for valid device */ + if ((!dev) || (!dev->drm.name) || (dev->tty.fd < 0)) return EINA_FALSE; + + /* send ioctl for vt acquire */ + if (ioctl(dev->tty.fd, VT_RELDISP, VT_ACKACQ) < 0) return EINA_FALSE; + + return EINA_TRUE; +}