Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

index.js 4.2KB

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