| /* |
| * BRLTTY - A background process providing access to the console screen (when in |
| * text mode) for a blind person using a refreshable braille display. |
| * |
| * Copyright (C) 1995-2023 by The BRLTTY Developers. |
| * |
| * BRLTTY comes with ABSOLUTELY NO WARRANTY. |
| * |
| * This is free software, placed under the terms of the |
| * GNU Lesser General Public License, as published by the Free Software |
| * Foundation; either version 2.1 of the License, or (at your option) any |
| * later version. Please see the file LICENSE-LGPL for details. |
| * |
| * Web Page: http://brltty.app/ |
| * |
| * This software is maintained by Dave Mielke <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "log.h" |
| #include "parameters.h" |
| #include "device.h" |
| #include "get_select.h" |
| #include "async_alarm.h" |
| #include "scr.h" |
| #include "scr_gpm.h" |
| |
| #ifdef HAVE_LIBGPM |
| #include <gpm.h> |
| extern int gpm_tried; |
| |
| typedef enum { |
| GCS_CLOSED, |
| GCS_FAILED, |
| GCS_OPENED |
| } GpmConnectionState; |
| |
| static int gpmConnectionState = GCS_CLOSED; |
| |
| ASYNC_ALARM_CALLBACK(gpmResetConnection) { |
| gpmConnectionState = GCS_CLOSED; |
| } |
| |
| static int |
| gpmOpenConnection (void) { |
| switch (gpmConnectionState) { |
| case GCS_CLOSED: { |
| Gpm_Connect options = { |
| .eventMask = GPM_MOVE, |
| .defaultMask = ~0, |
| .minMod = 0, |
| .maxMod = ~0 |
| }; |
| |
| gpm_tried = 0; |
| gpm_zerobased = 1; |
| |
| if (Gpm_Open(&options, -1) == -1) { |
| logMessage(LOG_DEBUG, "GPM open error: %s", strerror(errno)); |
| asyncNewRelativeAlarm(NULL, GPM_CONNECTION_RESET_DELAY, gpmResetConnection, NULL); |
| gpmConnectionState = GCS_FAILED; |
| return 0; |
| } |
| |
| logMessage(LOG_DEBUG, "GPM opened: fd=%d con=%d", gpm_fd, gpm_consolefd); |
| gpmConnectionState = GCS_OPENED; |
| } |
| |
| case GCS_OPENED: |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| gpmCloseConnection (int alreadyClosed) { |
| if (gpmConnectionState == GCS_OPENED) { |
| if (!alreadyClosed) Gpm_Close(); |
| logMessage(LOG_DEBUG, "GPM closed"); |
| } |
| gpmConnectionState = GCS_CLOSED; |
| } |
| #endif /* HAVE_LIBGPM */ |
| |
| static int |
| gpmScreenHandler_highlightRegion (int left, int right, int top, int bottom) { |
| #ifdef HAVE_LIBGPM |
| FILE *console = getConsole(); |
| |
| if (console) { |
| if (gpmOpenConnection() && (gpm_fd >= 0)) { |
| if (Gpm_DrawPointer(left, top, fileno(console)) != -1) return 1; |
| |
| if (errno != EINVAL) { |
| logMessage(LOG_DEBUG, "Gpm_DrawPointer error: %s", strerror(errno)); |
| gpmCloseConnection(0); |
| return 0; |
| } |
| } |
| } |
| #endif /* HAVE_LIBGPM */ |
| |
| return 0; |
| } |
| |
| static int |
| gpmScreenHandler_getPointer (int *column, int *row) { |
| int ok = 0; |
| |
| #ifdef HAVE_LIBGPM |
| if (gpmOpenConnection()) { |
| if (gpm_fd >= 0) { |
| int error = 0; |
| |
| while (1) { |
| fd_set mask; |
| struct timeval timeout; |
| Gpm_Event event; |
| int result; |
| |
| FD_ZERO(&mask); |
| FD_SET(gpm_fd, &mask); |
| memset(&timeout, 0, sizeof(timeout)); |
| |
| if ((result = select(gpm_fd+1, &mask, NULL, NULL, &timeout)) == 0) break; |
| error = 1; |
| |
| if (result == -1) { |
| if (errno == EINTR) continue; |
| logSystemError("select"); |
| break; |
| } |
| |
| if (!FD_ISSET(gpm_fd, &mask)) { |
| logMessage(LOG_DEBUG, "GPM file descriptor not set: %d", gpm_fd); |
| break; |
| } |
| |
| if ((result = Gpm_GetEvent(&event)) == -1) { |
| if (errno == EINTR) continue; |
| logSystemError("Gpm_GetEvent"); |
| break; |
| } |
| |
| error = 0; |
| if (result == 0) { |
| gpmCloseConnection(1); |
| break; |
| } |
| |
| *column = event.x; |
| *row = event.y; |
| ok = 1; |
| } |
| |
| if (error) gpmCloseConnection(0); |
| } |
| } |
| #endif /* HAVE_LIBGPM */ |
| |
| return ok; |
| } |
| |
| void |
| gpmIncludeScreenHandlers (MainScreen *main) { |
| main->base.highlightRegion = gpmScreenHandler_highlightRegion; |
| main->base.getPointer = gpmScreenHandler_getPointer; |
| } |