busybox at busybox.net

Tammo Block tammo.block at gmail.com
Tue Jun 23 19:18:52 UTC 2020


Hi everybody,

this patch adds a mouse daemon applet to busybox.

The behavior is different from the well known gpm [1] in two aspects:
1.) Only the PS/2 mouse protocol is supported (This includes USB mice).
2.) The nonstandard gpm devices gpmdata/gpmctl are not supported.
Due to these differences I choose not to call the applet "gpm".

This daemon more or less mimics the behavior of consolation [2], a rather
recent gpm replacement, using libinput for device discovery and readout.

You should be able to test moused by just starting it without options and
start to copy/paste text on the console. Pressing left button on the same
place several times switches between selecting characters, words and lines.

Left button:   Start selection or select single character/word/line
Middle button: Paste selection
Right button:  Set end of selection of multiple characters/words/lines

The mouse report kernel features can be tested with the vttest program.
(Select 11 -> 8 -> 5 -> 4 in the menu)


The bloat-o-meter says :

function                                             old     new   delta
moused_main                                            -     978    +978
.rodata                                           162718  163048    +330
packed_usage                                       33700   33803    +103
applet_main                                         3176    3184      +8
applet_names                                        2733    2740      +7
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 4/0 up/down: 1426/0)           Total: 1426 bytes


There are probably many places to improve the code, but my first question is:
Could this be interesting at all for busybox?


Kind regards,
Tammo


[1] https://github.com/telmich/gpm
[2] https://salsa.debian.org/consolation-team/consolation/


diff --git a/miscutils/moused.c b/miscutils/moused.c
new file mode 100644
index 000000000..2681fbd6d
--- /dev/null
+++ b/miscutils/moused.c
@@ -0,0 +1,198 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * simple mouse daemon
+ *
+ * Copyright (C) 2020 by Tammo Block (tammo.block at gmail.com)
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * Yet moused only supports ps2 style mice (and therefore only 3 buttons)
+ * The gpm protocols (gpmdata/gpmctl) are not supported
+ *
+ */
+
+//config:config MOUSED
+//config:      bool "moused (1.4 kb) (NEW)"
+//config:      default n
+//config:      help
+//config:      Simple mouse daemon. Reports mouse events to console
applications.
+
+//applet:IF_MOUSED(APPLET(moused, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MOUSED) += moused.o
+
+//usage:#define moused_trivial_usage
+//usage:       "[OPTS]"
+//usage:#define moused_full_usage "\n\n"
+//usage:       "Enable console mouse support for PS/2 like mice.\n"
+//usage:       "-f      Do not fork to background (default is to fork)\n"
+//usage:       "-r      Disable mouse event reports to kernel console\n"
+//usage:       "-s      Disable console selection support\n"
+//usage:       "-a VAL  Set mouse speed, higher means slow (defaults to 10)\n"
+//usage:       "-m DEV  Mouse device (defaults to /dev/input/mice)\n"
+
+#include "libbb.h"
+#include <linux/kd.h>
+#include <linux/tiocl.h>
+
+int moused_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int moused_main(int argc UNUSED_PARAM, char **argv)
+{
+       struct winsize screen;
+       struct pollfd pfd;
+       signed int xpxl=1, ypxl=1;
+       unsigned int ttyfd, ret, opts;
+       unsigned short lastx=1, lasty=1, speed;
+       unsigned char last=0, mode=0, btn=3, request = TIOCL_GETMOUSEREPORTING;
+       const char *device = "/dev/input/mice";
+       const char *speeds = "10";
+
+       /* PS2 mouse ps2event*/
+       struct {
+               unsigned char btn:4;
+               unsigned char ovl:4;
+               signed char x;
+               signed char y;
+       } ps2ev;
+
+       /* Internal state, layed out for ioctl */
+       struct {
+               char unused; /* Force struct alignment! */
+               char call;
+               struct tiocl_selection s;
+       } state;
+
+       opts = getopt32(argv, "frsa:m:", &speeds, &device);
+
+       speed = atoi(speeds);
+       if ( (speed > 10000) || (speed < 1) )
+               bb_simple_error_msg_and_die("Invalid speed value");
+
+       if (!(opts & 1))
+               bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
+
+       pfd.fd = open(device,O_RDONLY);
+       if (pfd.fd < 0)
+               bb_simple_perror_msg_and_die("Cannot open mouse device");
+
+       write_pidfile_std_path_and_ext("moused");
+
+       bb_signals(BB_FATAL_SIGS, record_signo);
+       pfd.events = POLLIN;
+
+       state.unused = 0;
+       state.call = TIOCL_SETSEL;
+
+       while (1) {
+               if (bb_got_signal)
+                       break;
+               ret = poll(&pfd, 1, -1);
+               if (ret < 1)
+                      break;
+
+               ret = read(pfd.fd, &ps2ev, sizeof(ps2ev));
+               if ( ret != sizeof(ps2ev)) {
+                       bb_simple_error_msg("Wrong data size!\n");
+                       continue;
+               }
+               if ( ! (ps2ev.btn | 8)) {
+                       bb_simple_error_msg("No PS2 data?\n");
+                       continue;
+               }
+               if (ps2ev.ovl & 12) {
+                       bb_simple_error_msg("Mouse data overflow! Ignoring.\n");
+                       continue;
+               }
+
+               ttyfd = open("/dev/tty0",O_RDONLY);
+               if (ttyfd < 0)
+                       bb_simple_perror_msg_and_die("Cannot open /dev/tty0");
+               if (ioctl(ttyfd, KDGETMODE, &ret))
+                       bb_simple_perror_msg_and_die("KDGETMODE");
+               if ( ret != KD_TEXT ) {
+                       close (ttyfd);
+                       continue;
+               }
+               if (ioctl(ttyfd, TIOCGWINSZ, &screen))
+                       bb_simple_perror_msg_and_die("TIOCGWINSZ");
+
+               /* Convert PS2 mouse units to screen position */
+               xpxl += ps2ev.x;
+               ypxl -= ps2ev.y; /* Y axis is inverted for PS2 protocol*/
+               if (xpxl < 1)
+                       xpxl = 1;
+               if (ypxl < 1)
+                       ypxl = 1;
+               if (xpxl > screen.ws_col * speed )
+                       xpxl = screen.ws_col * speed;
+               if (ypxl > screen.ws_row * speed )
+                       ypxl = screen.ws_row * speed;
+               state.s.xe = state.s.xs = (xpxl / speed) + 1;
+               state.s.ye = state.s.ys = (ypxl / speed) + 1;
+               state.s.sel_mode = TIOCL_SELPOINTER;
+
+               /* Convert PS2 mouse buttons.
+                * We only care for the _lowest_ pressed button
+                * A value of 3 means no button pressed
+                */
+               if ( ps2ev.btn & 1)
+                       btn = 0;
+               else if ( ps2ev.btn & 4)
+                       btn = 1;
+               else if ( ps2ev.btn & 2)
+                       btn = 2;
+               else
+                       btn = 3;
+
+               request = TIOCL_GETMOUSEREPORTING;
+               if (ioctl(ttyfd, TIOCLINUX, &request))
+                       bb_simple_perror_msg_and_die("TIOCL_GETMOUSEREPORT");
+
+               /* Kernel asks for mouse report */
+               if ( request && !(opts & 2) ) {
+                       if ( btn != last ) {
+                               state.s.sel_mode = ( btn |
TIOCL_SELMOUSEREPORT );
+                               if (ioctl(ttyfd, TIOCLINUX, &state.call ))
+
bb_simple_perror_msg("TIOCL_SELMOUSEREPORT");
+                               state.s.sel_mode = TIOCL_SELPOINTER;
+                       }
+               /* Copy/Paste mode, no mouse report */
+               } else if ( !(opts & 4) ) {
+                       if ( btn == last ) {
+                               if ( btn != 3) {
+                                       state.s.xe = lastx;
+                                       state.s.ye = lasty;
+                                       state.s.sel_mode = mode;
+                               }
+                       } else if (btn < last) {
+                               if (btn == 0) {
+                                       if ((state.s.xs == lastx) &&
(state.s.ys == lasty))
+                                               mode = (mode + 1) % 3;
+                                       else
+                                               mode = 0;
+                                       state.s.sel_mode = mode;
+                                       lastx = state.s.xs;
+                                       lasty = state.s.ys;
+                               } else if (btn == 1) {
+                                       request = TIOCL_PASTESEL;
+                                       if (ioctl(ttyfd, TIOCLINUX, &request)<0)
+
bb_simple_perror_msg("TIOCL_PASTESEL");
+                                       mode = 0;
+                               } else if (btn == 2) {
+                                       state.s.xe = lastx;
+                                       state.s.ye = lasty;
+                                       state.s.sel_mode = mode;
+                               }
+                       }
+               }
+               if (ioctl(ttyfd, TIOCLINUX, &state.call ))
+                       bb_simple_perror_msg("TIOCL_SETSEL");
+               last = btn;
+               close (ttyfd);
+       } // while (1)
+       close (pfd.fd);
+       return bb_got_signal;
+}
+


More information about the busybox mailing list