Mouse.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <?php
  2. /**
  3. * Hoa
  4. *
  5. *
  6. * @license
  7. *
  8. * New BSD License
  9. *
  10. * Copyright © 2007-2017, Hoa community. All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. * * Neither the name of the Hoa nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software without
  21. * specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. namespace Hoa\Console;
  36. use Hoa\Consistency;
  37. use Hoa\Event;
  38. /**
  39. * Class \Hoa\Console\Mouse.
  40. *
  41. * Allow to listen the mouse.
  42. *
  43. * @copyright Copyright © 2007-2017 Hoa community
  44. * @license New BSD License
  45. */
  46. class Mouse implements Event\Listenable
  47. {
  48. use Event\Listens;
  49. /**
  50. * Pointer code for left button.
  51. *
  52. * @const int
  53. */
  54. const BUTTON_LEFT = 0;
  55. /**
  56. * Pointer code for the middle button.
  57. *
  58. * @const int
  59. */
  60. const BUTTON_MIDDLE = 1;
  61. /**
  62. * Pointer code for the right button.
  63. *
  64. * @const int
  65. */
  66. const BUTTON_RIGHT = 2;
  67. /**
  68. * Pointer code for the release of the button.
  69. *
  70. * @const int
  71. */
  72. const BUTTON_RELEASE = 3;
  73. /**
  74. * Pointer code for the wheel up.
  75. *
  76. * @const int
  77. */
  78. const WHEEL_UP = 64;
  79. /**
  80. * Pointer code for the wheel down.
  81. *
  82. * @const int
  83. */
  84. const WHEEL_DOWN = 65;
  85. /**
  86. * Singleton.
  87. *
  88. * @var \Hoa\Console\Mouse
  89. */
  90. protected static $_instance = null;
  91. /**
  92. * Whether the mouse is tracked or not.
  93. *
  94. * @var bool
  95. */
  96. protected static $_enabled = false;
  97. /**
  98. * Constructor.
  99. *
  100. */
  101. private function __construct()
  102. {
  103. $this->setListener(
  104. new Event\Listener(
  105. $this,
  106. [
  107. 'mouseup',
  108. 'mousedown',
  109. 'wheelup',
  110. 'wheeldown',
  111. ]
  112. )
  113. );
  114. return;
  115. }
  116. /**
  117. * Singleton.
  118. *
  119. * @return \Hoa\Console\Mouse
  120. */
  121. public static function getInstance()
  122. {
  123. if (null === static::$_instance) {
  124. static::$_instance = new static();
  125. }
  126. return static::$_instance;
  127. }
  128. /**
  129. * Track the mouse.
  130. *
  131. * @return bool
  132. */
  133. public static function track()
  134. {
  135. if (true === static::$_enabled) {
  136. return;
  137. }
  138. static::$_enabled = true;
  139. Console::getOutput()->writeAll(
  140. "\033[1;2'z" .
  141. "\033[?1000h" .
  142. "\033[?1003h"
  143. );
  144. $instance = static::getInstance();
  145. $bucket = [
  146. 'x' => 0,
  147. 'y' => 0,
  148. 'button' => null,
  149. 'shift' => false,
  150. 'meta' => false,
  151. 'ctrl' => false
  152. ];
  153. $input = Console::getInput();
  154. $read = [$input->getStream()->getStream()];
  155. while (true) {
  156. if (false === @stream_select($read, $write, $except, 30)) {
  157. static::untrack();
  158. break;
  159. }
  160. $string = $input->readCharacter();
  161. if ("\033" !== $string) {
  162. continue;
  163. }
  164. $char = $input->readCharacter();
  165. if ('[' !== $char) {
  166. continue;
  167. }
  168. $char = $input->readCharacter();
  169. if ('M' !== $char) {
  170. continue;
  171. }
  172. $data = $input->read(3);
  173. $cb = ord($data[0]);
  174. $cx = ord($data[1]) - 32;
  175. $cy = ord($data[2]) - 32;
  176. $bucket['x'] = $cx;
  177. $bucket['y'] = $cy;
  178. $bucket['shift'] = 0 !== ($cb & 4);
  179. $bucket['meta'] = 0 !== ($cb & 8);
  180. $bucket['ctrl'] = 0 !== ($cb & 16);
  181. $cb = ($cb | 28) ^ 28; // 28 = 4 | 8 | 16
  182. $cb -= 32;
  183. switch ($cb) {
  184. case static::WHEEL_UP:
  185. $instance->getListener()->fire(
  186. 'wheelup',
  187. new Event\Bucket($bucket)
  188. );
  189. break;
  190. case static::WHEEL_DOWN:
  191. $instance->getListener()->fire(
  192. 'wheeldown',
  193. new Event\Bucket($bucket)
  194. );
  195. break;
  196. case static::BUTTON_RELEASE:
  197. $instance->getListener()->fire(
  198. 'mouseup',
  199. new Event\Bucket($bucket)
  200. );
  201. $bucket['button'] = null;
  202. break;
  203. default:
  204. if (static::BUTTON_LEFT === $cb) {
  205. $bucket['button'] = 'left';
  206. } elseif (static::BUTTON_MIDDLE === $cb) {
  207. $bucket['button'] = 'middle';
  208. } elseif (static::BUTTON_RIGHT === $cb) {
  209. $bucket['button'] = 'right';
  210. } else {
  211. // hover
  212. continue 2;
  213. }
  214. $instance->getListener()->fire(
  215. 'mousedown',
  216. new Event\Bucket($bucket)
  217. );
  218. }
  219. }
  220. return;
  221. }
  222. /**
  223. * Untrack the mouse.
  224. *
  225. * @return void
  226. */
  227. public static function untrack()
  228. {
  229. if (false === static::$_enabled) {
  230. return;
  231. }
  232. Console::getOutput()->writeAll(
  233. "\033[?1003l" .
  234. "\033[?1000l"
  235. );
  236. static::$_enabled = false;
  237. return;
  238. }
  239. }
  240. /**
  241. * Advanced interaction.
  242. */
  243. Console::advancedInteraction();
  244. /**
  245. * Untrack mouse.
  246. */
  247. Consistency::registerShutdownFunction(xcallable('Hoa\Console\Mouse::untrack'));