You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. var udev = require("../../udev");
  2. var spawn = require("child_process").spawn;
  3. var exec = require("child_process").exec;
  4. var debounce = require("../../js/debounce");
  5. exports.start = start;
  6. exports.stop = stop;
  7. exports.event = event;
  8. var conf;
  9. var logger;
  10. var modules;
  11. // Set an xinput property
  12. function setProp(name, val, suppressWarnings) {
  13. var args = [ "--set-prop", name ];
  14. val.forEach(v => args.push(v));
  15. // We need to wait for X to recognize the device
  16. setTimeout(function() {
  17. var child = spawn("xinput", args)
  18. child.on("close", code => {
  19. if (code !== 0 && !suppressWarnings)
  20. logger.warn("Xinput command failed:", args.join(", "));
  21. });
  22. if (!suppressWarnings) {
  23. child.stderr.on("data", d => {
  24. logger.warn(
  25. "Xinput command for", name, "failed:",
  26. d.toString().trim());
  27. });
  28. }
  29. }, 500);
  30. }
  31. // Queued commands will be run once runCmds is called
  32. var queuedCmds = [];
  33. function queueCmd(command) {
  34. if (queuedCmds.indexOf(command) === -1)
  35. queuedCmds.push(command);
  36. }
  37. // Run queued commands, debounced
  38. var runCmds = debounce(function() {
  39. queuedCmds.forEach(cmd => {
  40. var child = exec(cmd);
  41. child.on("close", code => {
  42. if (code !== 0)
  43. logger.warn("Command failed:", cmd);
  44. });
  45. });
  46. queuedCmds = [];
  47. });
  48. // Devices which aren't keyboards or mice with names aren't interesting
  49. function filter(dev) {
  50. return dev.NAME &&
  51. (dev.ID_INPUT_KEYBOARD || dev.ID_INPUT_MOUSE || dev.ID_INPUT_TOUCHPAD);
  52. }
  53. // name can be either an array or a string
  54. function nameMatches(dev, name) {
  55. // Remove quotes form device name
  56. var devname = dev.NAME.substring(1, dev.NAME.length - 1);
  57. if (typeof name === "string") {
  58. if (name !== "*" && name !== devname)
  59. return false;
  60. } else if (name instanceof Array) {
  61. var matched = false;
  62. for (var i in name) {
  63. if (name === devname) {
  64. matched = true;
  65. break;
  66. }
  67. }
  68. if (!matched)
  69. return false;
  70. } else {
  71. logger.warning("Expected name to be string or array");
  72. return false;
  73. }
  74. return true;
  75. }
  76. function onchange(dev) {
  77. if (!filter(dev))
  78. return;
  79. var isKeyboard = !!dev.ID_INPUT_KEYBOARD;
  80. var isPointer = !!(dev.ID_INPUT_MOUSE || dev.ID_INPUT_TOUCHPAD);
  81. // Find out what to log
  82. var inputType;
  83. if (isKeyboard && isPointer)
  84. inputType = "keyboard/pointer";
  85. else if (isKeyboard)
  86. inputType = "keyboard";
  87. else if (isPointer)
  88. inputType = "mouse";
  89. // Log add/change
  90. if (dev.ACTION === "add")
  91. logger.info(inputType, dev.NAME, "added");
  92. else if (dev.ACTION === "change")
  93. logger.info(inputType, dev.NAME, "changed");
  94. // Run through and apply relevant rules
  95. conf.forEach(entry => {
  96. if (entry.type === "pointer" && !isPointer)
  97. return;
  98. if (entry.type === "keyboard" && !isKeyboard)
  99. return;
  100. if (!nameMatches(dev, entry.name))
  101. return;
  102. // Add pointer: or keyboard: to name, and remove quotes
  103. var name = dev.NAME.substring(1, dev.NAME.length - 1);
  104. if (entry.type === "pointer")
  105. name = "pointer:"+name;
  106. else if (entry.type === "keyboard")
  107. name = "keyboard:"+name
  108. else
  109. return log.error("Invalid input type: "+entry.type);
  110. // If the entry matches everything, we don't need to print xinput warnings
  111. var suppressWarnings = entry.name === "*";
  112. // Set xinput options
  113. if (entry.options)
  114. entry.options.forEach(prop => setProp(name, prop, suppressWarnings));
  115. // Run commands
  116. if (entry.commands)
  117. entry.commands.forEach(queueCmd);
  118. });
  119. // Run queued commands
  120. runCmds();
  121. }
  122. function start(conf_, logger_, modules_) {
  123. conf = conf_ || conf;
  124. logger = logger_ || logger;
  125. modules = modules_ || modules;
  126. udev.list("input", devs => devs.forEach(onchange))
  127. udev.monitor("input", onchange);
  128. }
  129. function stop(cb) {
  130. udev.unmonitor("input", onchange);
  131. cb();
  132. }
  133. function event(name, ...params) {
  134. switch (name) {
  135. default:
  136. logger.warn("Unknown event: "+name);
  137. }
  138. }