xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

inputthread.c (15182B)


      1 /* inputthread.c -- Threaded generation of input events.
      2  *
      3  * Copyright © 2007-2008 Tiago Vignatti <vignatti at freedesktop org>
      4  * Copyright © 2010 Nokia
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  * Authors: Fernando Carrijo <fcarrijo at freedesktop org>
     25  *          Tiago Vignatti <vignatti at freedesktop org>
     26  */
     27 
     28 #ifdef HAVE_DIX_CONFIG_H
     29 #include <dix-config.h>
     30 #endif
     31 
     32 #include <stdio.h>
     33 #include <errno.h>
     34 #include <stdlib.h>
     35 #include <unistd.h>
     36 #include <pthread.h>
     37 
     38 #include "inputstr.h"
     39 #include "opaque.h"
     40 #include "osdep.h"
     41 
     42 #if INPUTTHREAD
     43 
     44 Bool InputThreadEnable = TRUE;
     45 
     46 /**
     47  * An input device as seen by the threaded input facility
     48  */
     49 
     50 typedef enum _InputDeviceState {
     51     device_state_added,
     52     device_state_running,
     53     device_state_removed
     54 } InputDeviceState;
     55 
     56 typedef struct _InputThreadDevice {
     57     struct xorg_list node;
     58     NotifyFdProcPtr readInputProc;
     59     void *readInputArgs;
     60     int fd;
     61     InputDeviceState state;
     62 } InputThreadDevice;
     63 
     64 /**
     65  * The threaded input facility.
     66  *
     67  * For now, we have one instance for all input devices.
     68  */
     69 typedef struct {
     70     pthread_t thread;
     71     struct xorg_list devs;
     72     struct ospoll *fds;
     73     int readPipe;
     74     int writePipe;
     75     Bool changed;
     76     Bool running;
     77 } InputThreadInfo;
     78 
     79 static InputThreadInfo *inputThreadInfo;
     80 
     81 static int hotplugPipeRead = -1;
     82 static int hotplugPipeWrite = -1;
     83 
     84 static int input_mutex_count;
     85 
     86 #ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
     87 static pthread_mutex_t input_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
     88 #else
     89 static pthread_mutex_t input_mutex;
     90 static Bool input_mutex_initialized;
     91 #endif
     92 
     93 int
     94 in_input_thread(void)
     95 {
     96     return inputThreadInfo &&
     97            pthread_equal(pthread_self(), inputThreadInfo->thread);
     98 }
     99 
    100 void
    101 input_lock(void)
    102 {
    103 #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
    104     if (!input_mutex_initialized) {
    105         pthread_mutexattr_t mutex_attr;
    106 
    107         input_mutex_initialized = TRUE;
    108         pthread_mutexattr_init(&mutex_attr);
    109         pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
    110         pthread_mutex_init(&input_mutex, &mutex_attr);
    111     }
    112 #endif
    113     pthread_mutex_lock(&input_mutex);
    114     ++input_mutex_count;
    115 }
    116 
    117 void
    118 input_unlock(void)
    119 {
    120     --input_mutex_count;
    121     pthread_mutex_unlock(&input_mutex);
    122 }
    123 
    124 void
    125 input_force_unlock(void)
    126 {
    127     if (pthread_mutex_trylock(&input_mutex) == 0) {
    128         input_mutex_count++;
    129         /* unlock +1 times for the trylock */
    130         while (input_mutex_count > 0)
    131             input_unlock();
    132     }
    133 }
    134 
    135 /**
    136  * Notify a thread about the availability of new asynchronously enqueued input
    137  * events.
    138  *
    139  * @see WaitForSomething()
    140  */
    141 static void
    142 InputThreadFillPipe(int writeHead)
    143 {
    144     int ret;
    145     char byte = 0;
    146 
    147     do {
    148         ret = write(writeHead, &byte, 1);
    149     } while (ret < 0 && ETEST(errno));
    150 }
    151 
    152 /**
    153  * Consume eventual notifications left by a thread.
    154  *
    155  * @see WaitForSomething()
    156  * @see InputThreadFillPipe()
    157  */
    158 static int
    159 InputThreadReadPipe(int readHead)
    160 {
    161     int ret, array[10];
    162 
    163     ret = read(readHead, &array, sizeof(array));
    164     if (ret >= 0)
    165         return ret;
    166 
    167     if (errno != EAGAIN)
    168         FatalError("input-thread: draining pipe (%d)", errno);
    169 
    170     return 1;
    171 }
    172 
    173 static void
    174 InputReady(int fd, int xevents, void *data)
    175 {
    176     InputThreadDevice *dev = data;
    177 
    178     input_lock();
    179     if (dev->state == device_state_running)
    180         dev->readInputProc(fd, xevents, dev->readInputArgs);
    181     input_unlock();
    182 }
    183 
    184 /**
    185  * Register an input device in the threaded input facility
    186  *
    187  * @param fd File descriptor which identifies the input device
    188  * @param readInputProc Procedure used to read input from the device
    189  * @param readInputArgs Arguments to be consumed by the above procedure
    190  *
    191  * return 1 if success; 0 otherwise.
    192  */
    193 int
    194 InputThreadRegisterDev(int fd,
    195                        NotifyFdProcPtr readInputProc,
    196                        void *readInputArgs)
    197 {
    198     InputThreadDevice *dev, *old;
    199 
    200     if (!inputThreadInfo)
    201         return SetNotifyFd(fd, readInputProc, X_NOTIFY_READ, readInputArgs);
    202 
    203     input_lock();
    204 
    205     dev = NULL;
    206     xorg_list_for_each_entry(old, &inputThreadInfo->devs, node) {
    207         if (old->fd == fd && old->state != device_state_removed) {
    208             dev = old;
    209             break;
    210         }
    211     }
    212 
    213     if (dev) {
    214         dev->readInputProc = readInputProc;
    215         dev->readInputArgs = readInputArgs;
    216     } else {
    217         dev = calloc(1, sizeof(InputThreadDevice));
    218         if (dev == NULL) {
    219             DebugF("input-thread: could not register device\n");
    220             input_unlock();
    221             return 0;
    222         }
    223 
    224         dev->fd = fd;
    225         dev->readInputProc = readInputProc;
    226         dev->readInputArgs = readInputArgs;
    227         dev->state = device_state_added;
    228 
    229         /* Do not prepend, so that any dev->state == device_state_removed
    230          * with the same dev->fd get processed first. */
    231         xorg_list_append(&dev->node, &inputThreadInfo->devs);
    232     }
    233 
    234     inputThreadInfo->changed = TRUE;
    235 
    236     input_unlock();
    237 
    238     DebugF("input-thread: registered device %d\n", fd);
    239     InputThreadFillPipe(hotplugPipeWrite);
    240 
    241     return 1;
    242 }
    243 
    244 /**
    245  * Unregister a device in the threaded input facility
    246  *
    247  * @param fd File descriptor which identifies the input device
    248  *
    249  * @return 1 if success; 0 otherwise.
    250  */
    251 int
    252 InputThreadUnregisterDev(int fd)
    253 {
    254     InputThreadDevice *dev;
    255     Bool found_device = FALSE;
    256 
    257     /* return silently if input thread is already finished (e.g., at
    258      * DisableDevice time, evdev tries to call this function again through
    259      * xf86RemoveEnabledDevice) */
    260     if (!inputThreadInfo) {
    261         RemoveNotifyFd(fd);
    262         return 1;
    263     }
    264 
    265     input_lock();
    266     xorg_list_for_each_entry(dev, &inputThreadInfo->devs, node)
    267         if (dev->fd == fd) {
    268             found_device = TRUE;
    269             break;
    270         }
    271 
    272     /* fd didn't match any registered device. */
    273     if (!found_device) {
    274         input_unlock();
    275         return 0;
    276     }
    277 
    278     dev->state = device_state_removed;
    279     inputThreadInfo->changed = TRUE;
    280 
    281     input_unlock();
    282 
    283     InputThreadFillPipe(hotplugPipeWrite);
    284     DebugF("input-thread: unregistered device: %d\n", fd);
    285 
    286     return 1;
    287 }
    288 
    289 static void
    290 InputThreadPipeNotify(int fd, int revents, void *data)
    291 {
    292     /* Empty pending input, shut down if the pipe has been closed */
    293     if (InputThreadReadPipe(hotplugPipeRead) == 0) {
    294         inputThreadInfo->running = FALSE;
    295     }
    296 }
    297 
    298 /**
    299  * The workhorse of threaded input event generation.
    300  *
    301  * Or if you prefer: The WaitForSomething for input devices. :)
    302  *
    303  * Runs in parallel with the server main thread, listening to input devices in
    304  * an endless loop. Whenever new input data is made available, calls the
    305  * proper device driver's routines which are ultimately responsible for the
    306  * generation of input events.
    307  *
    308  * @see InputThreadPreInit()
    309  * @see InputThreadInit()
    310  */
    311 
    312 static void*
    313 InputThreadDoWork(void *arg)
    314 {
    315     sigset_t set;
    316 
    317     /* Don't handle any signals on this thread */
    318     sigfillset(&set);
    319     pthread_sigmask(SIG_BLOCK, &set, NULL);
    320 
    321     ddxInputThreadInit();
    322 
    323     inputThreadInfo->running = TRUE;
    324 
    325 #if defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)
    326     pthread_setname_np (pthread_self(), "InputThread");
    327 #elif defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID)
    328     pthread_setname_np ("InputThread");
    329 #endif
    330 
    331     ospoll_add(inputThreadInfo->fds, hotplugPipeRead,
    332                ospoll_trigger_level,
    333                InputThreadPipeNotify,
    334                NULL);
    335     ospoll_listen(inputThreadInfo->fds, hotplugPipeRead, X_NOTIFY_READ);
    336 
    337     while (inputThreadInfo->running)
    338     {
    339         DebugF("input-thread: %s waiting for devices\n", __func__);
    340 
    341         /* Check for hotplug changes and modify the ospoll structure to suit */
    342         if (inputThreadInfo->changed) {
    343             InputThreadDevice *dev, *tmp;
    344 
    345             input_lock();
    346             inputThreadInfo->changed = FALSE;
    347             xorg_list_for_each_entry_safe(dev, tmp, &inputThreadInfo->devs, node) {
    348                 switch (dev->state) {
    349                 case device_state_added:
    350                     ospoll_add(inputThreadInfo->fds, dev->fd,
    351                                ospoll_trigger_level,
    352                                InputReady,
    353                                dev);
    354                     ospoll_listen(inputThreadInfo->fds, dev->fd, X_NOTIFY_READ);
    355                     dev->state = device_state_running;
    356                     break;
    357                 case device_state_running:
    358                     break;
    359                 case device_state_removed:
    360                     ospoll_remove(inputThreadInfo->fds, dev->fd);
    361                     xorg_list_del(&dev->node);
    362                     free(dev);
    363                     break;
    364                 }
    365             }
    366             input_unlock();
    367         }
    368 
    369         if (ospoll_wait(inputThreadInfo->fds, -1) < 0) {
    370             if (errno == EINVAL)
    371                 FatalError("input-thread: %s (%s)", __func__, strerror(errno));
    372             else if (errno != EINTR)
    373                 ErrorF("input-thread: %s (%s)\n", __func__, strerror(errno));
    374         }
    375 
    376         /* Kick main thread to process the generated input events and drain
    377          * events from hotplug pipe */
    378         InputThreadFillPipe(inputThreadInfo->writePipe);
    379     }
    380 
    381     ospoll_remove(inputThreadInfo->fds, hotplugPipeRead);
    382 
    383     return NULL;
    384 }
    385 
    386 static void
    387 InputThreadNotifyPipe(int fd, int mask, void *data)
    388 {
    389     InputThreadReadPipe(fd);
    390 }
    391 
    392 /**
    393  * Pre-initialize the facility used for threaded generation of input events
    394  *
    395  */
    396 void
    397 InputThreadPreInit(void)
    398 {
    399     int fds[2], hotplugPipe[2];
    400     int flags;
    401 
    402     if (!InputThreadEnable)
    403         return;
    404 
    405     if (pipe(fds) < 0)
    406         FatalError("input-thread: could not create pipe");
    407 
    408      if (pipe(hotplugPipe) < 0)
    409         FatalError("input-thread: could not create pipe");
    410 
    411     inputThreadInfo = malloc(sizeof(InputThreadInfo));
    412     if (!inputThreadInfo)
    413         FatalError("input-thread: could not allocate memory");
    414 
    415     inputThreadInfo->changed = FALSE;
    416 
    417     inputThreadInfo->thread = 0;
    418     xorg_list_init(&inputThreadInfo->devs);
    419     inputThreadInfo->fds = ospoll_create();
    420 
    421     /* By making read head non-blocking, we ensure that while the main thread
    422      * is busy servicing client requests, the dedicated input thread can work
    423      * in parallel.
    424      */
    425     inputThreadInfo->readPipe = fds[0];
    426     fcntl(inputThreadInfo->readPipe, F_SETFL, O_NONBLOCK);
    427     flags = fcntl(inputThreadInfo->readPipe, F_GETFD);
    428     if (flags != -1) {
    429         flags |= FD_CLOEXEC;
    430         (void)fcntl(inputThreadInfo->readPipe, F_SETFD, flags);
    431     }
    432     SetNotifyFd(inputThreadInfo->readPipe, InputThreadNotifyPipe, X_NOTIFY_READ, NULL);
    433 
    434     inputThreadInfo->writePipe = fds[1];
    435 
    436     hotplugPipeRead = hotplugPipe[0];
    437     fcntl(hotplugPipeRead, F_SETFL, O_NONBLOCK);
    438     flags = fcntl(hotplugPipeRead, F_GETFD);
    439     if (flags != -1) {
    440         flags |= FD_CLOEXEC;
    441         (void)fcntl(hotplugPipeRead, F_SETFD, flags);
    442     }
    443     hotplugPipeWrite = hotplugPipe[1];
    444 
    445 #ifndef __linux__ /* Linux does not deal well with renaming the main thread */
    446 #if defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)
    447     pthread_setname_np (pthread_self(), "MainThread");
    448 #elif defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID)
    449     pthread_setname_np ("MainThread");
    450 #endif
    451 #endif
    452 
    453 }
    454 
    455 /**
    456  * Start the threaded generation of input events. This routine complements what
    457  * was previously done by InputThreadPreInit(), being only responsible for
    458  * creating the dedicated input thread.
    459  *
    460  */
    461 void
    462 InputThreadInit(void)
    463 {
    464     pthread_attr_t attr;
    465 
    466     /* If the driver hasn't asked for input thread support by calling
    467      * InputThreadPreInit, then do nothing here
    468      */
    469     if (!inputThreadInfo)
    470         return;
    471 
    472     pthread_attr_init(&attr);
    473 
    474     /* For OSes that differentiate between processes and threads, the following
    475      * lines have sense. Linux uses the 1:1 thread model. The scheduler handles
    476      * every thread as a normal process. Therefore this probably has no meaning
    477      * if we are under Linux.
    478      */
    479     if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) != 0)
    480         ErrorF("input-thread: error setting thread scope\n");
    481 
    482     DebugF("input-thread: creating thread\n");
    483     pthread_create(&inputThreadInfo->thread, &attr,
    484                    &InputThreadDoWork, NULL);
    485 
    486     pthread_attr_destroy (&attr);
    487 }
    488 
    489 /**
    490  * Stop the threaded generation of input events
    491  *
    492  * This function is supposed to be called at server shutdown time only.
    493  */
    494 void
    495 InputThreadFini(void)
    496 {
    497     InputThreadDevice *dev, *next;
    498 
    499     if (!inputThreadInfo)
    500         return;
    501 
    502     /* Close the pipe to get the input thread to shut down */
    503     close(hotplugPipeWrite);
    504     input_force_unlock();
    505     pthread_join(inputThreadInfo->thread, NULL);
    506 
    507     xorg_list_for_each_entry_safe(dev, next, &inputThreadInfo->devs, node) {
    508         ospoll_remove(inputThreadInfo->fds, dev->fd);
    509         free(dev);
    510     }
    511     xorg_list_init(&inputThreadInfo->devs);
    512     ospoll_destroy(inputThreadInfo->fds);
    513 
    514     RemoveNotifyFd(inputThreadInfo->readPipe);
    515     close(inputThreadInfo->readPipe);
    516     close(inputThreadInfo->writePipe);
    517     inputThreadInfo->readPipe = -1;
    518     inputThreadInfo->writePipe = -1;
    519 
    520     close(hotplugPipeRead);
    521     hotplugPipeRead = -1;
    522     hotplugPipeWrite = -1;
    523 
    524     free(inputThreadInfo);
    525     inputThreadInfo = NULL;
    526 }
    527 
    528 int xthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)
    529 {
    530     return pthread_sigmask(how, set, oldset);
    531 }
    532 
    533 #else /* INPUTTHREAD */
    534 
    535 Bool InputThreadEnable = FALSE;
    536 
    537 void input_lock(void) {}
    538 void input_unlock(void) {}
    539 void input_force_unlock(void) {}
    540 
    541 void InputThreadPreInit(void) {}
    542 void InputThreadInit(void) {}
    543 void InputThreadFini(void) {}
    544 int in_input_thread(void) { return 0; }
    545 
    546 int InputThreadRegisterDev(int fd,
    547                            NotifyFdProcPtr readInputProc,
    548                            void *readInputArgs)
    549 {
    550     return SetNotifyFd(fd, readInputProc, X_NOTIFY_READ, readInputArgs);
    551 }
    552 
    553 extern int InputThreadUnregisterDev(int fd)
    554 {
    555     RemoveNotifyFd(fd);
    556     return 1;
    557 }
    558 
    559 int xthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)
    560 {
    561 #ifdef HAVE_SIGPROCMASK
    562     return sigprocmask(how, set, oldset);
    563 #else
    564     return 0;
    565 #endif
    566 }
    567 
    568 #endif