UNPKG

7.25 kBJavaScriptView Raw
1/*
2 Terminal Kit
3
4 Copyright (c) 2009 - 2020 Cédric Ronvel
5
6 The MIT License (MIT)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26
27"use strict" ;
28
29
30
31/*
32 This module try to stay close to the original GPM lib written in C.
33*/
34
35
36
37var net = require( 'net' ) ;
38var NextGenEvents = require( 'nextgen-events' ) ;
39var termkit = require( './termkit.js' ) ;
40
41var gpm = {} ;
42module.exports = gpm ;
43
44
45
46
47
48/* GPM structures & constants */
49/* as found in the GPM source code: src/headers/gpm.h */
50
51
52
53// Can't figure out the usage of the GPM_MAGIC constant ATM
54var gpmMagic = Buffer.allocUnsafe( 4 ) ;
55gpmMagic.writeUInt32LE( 0x47706D4C , 0 ) ;
56
57
58
59// Return a Buffer containing a Gpm_Connect structure, using a pid and a ttyIndex
60gpm.connectStructureBuffer = function connectStructureBuffer( gpmConnect ) {
61 var buffer = Buffer.allocUnsafe( 16 ) ;
62
63 if ( gpmConnect.eventMask === undefined ) { gpmConnect.eventMask = 65535 ; }
64 if ( gpmConnect.defaultMask === undefined ) { gpmConnect.defaultMask = 0 ; }
65 if ( gpmConnect.minMod === undefined ) { gpmConnect.minMod = 0 ; }
66 if ( gpmConnect.maxMod === undefined ) { gpmConnect.maxMod = 65535 ; }
67
68 //Looks like it want Little Endian
69 buffer.writeUInt16LE( gpmConnect.eventMask , 0 ) ; // eventMask: wanted events
70 buffer.writeUInt16LE( gpmConnect.defaultMask , 2 ) ; // defaultMask: things handled by default
71 buffer.writeUInt16LE( gpmConnect.minMod , 4 ) ; // minMod: want everything (modifier keys)
72 buffer.writeUInt16LE( gpmConnect.maxMod , 6 ) ; // maxMod: all modifiers keys included
73 buffer.writeUInt32LE( gpmConnect.pid , 8 ) ; // pid
74 buffer.writeUInt32LE( gpmConnect.vc , 12 ) ; // vc: the TTY index
75
76 //console.log( buffer ) ;
77
78 return buffer ;
79} ;
80
81
82
83// Extract a Gpm_Event from a Buffer
84gpm.eventStructure = function eventStructure( buffer ) {
85 var event = {} ;
86
87 //Looks like it is in Little Endian
88 event.buttons = buffer.readUInt8( 0 ) ;
89 event.modifiers = buffer.readUInt8( 1 ) ;
90
91 event.vc = buffer.readUInt16LE( 2 ) ;
92
93 event.dx = buffer.readInt16LE( 4 ) ;
94 event.dy = buffer.readInt16LE( 6 ) ;
95 event.x = buffer.readInt16LE( 8 ) ;
96 event.y = buffer.readInt16LE( 10 ) ;
97
98 event.eType = buffer.readUInt32LE( 12 ) ;
99
100 event.clicks = buffer.readUInt32LE( 16 ) ;
101
102 event.margin = buffer.readUInt32LE( 20 ) ;
103
104 event.wdx = buffer.readInt16LE( 24 ) ;
105 event.wdy = buffer.readInt16LE( 26 ) ;
106
107 //console.log( event ) ;
108
109 return event ;
110} ;
111
112
113
114//enum Gpm_Etype (comments are copy-paste of gpm.h)
115
116gpm.MOVE = 1 ;
117gpm.DRAG = 2 ; // exactly one of the bare ones is active at a time
118gpm.DOWN = 4 ;
119gpm.UP = 8 ;
120
121gpm.SINGLE = 16 ; // at most one in three is set
122gpm.DOUBLE = 32 ;
123gpm.TRIPLE = 64 ; // WARNING: I depend on the values
124
125gpm.MFLAG = 128 ; // motion during click?
126gpm.HARD = 256 ; // if set in the defaultMask, force an already used event to pass over to another handler
127
128gpm.ENTER = 512 ; // enter event, user in Roi's (Region Of Interest)
129gpm.LEAVE = 1024 ; // leave event, used in Roi's
130
131
132
133//enum Gpm_Margin
134
135gpm.TOP = 1 ;
136gpm.BOT = 2 ;
137gpm.LFT = 4 ;
138gpm.RGT = 8 ;
139
140
141
142
143
144/* GPM event handler */
145
146
147
148gpm.Handler = function Handler() { throw new Error( '[terminal] Cannot create a gpm.Handler object directly, use gpm.createHandler() instead' ) ; } ;
149gpm.Handler.prototype = Object.create( NextGenEvents.prototype ) ;
150gpm.Handler.prototype.constructor = gpm.Handler ;
151
152
153
154// Create a new GPM Handler
155gpm.createHandler = function createHandler( options ) {
156 if ( ! options || typeof options !== 'object' ) { options = {} ; }
157
158 if ( options.raw === undefined ) { options.raw = true ; }
159 if ( options.stdin === undefined ) { options.stdin = 0 ; /*process.stdin ;*/ }
160 if ( options.mode === undefined ) { options.mode = 'motion' ; }
161
162 var connectMode = { pid: process.pid } ;
163
164 connectMode.defaultMask =
165 gpm.MOVE | gpm.DRAG | gpm.DOWN | gpm.UP | gpm.SINGLE | gpm.DOUBLE | gpm.TRIPLE | gpm.MFLAG | gpm.HARD ;
166
167 switch ( options.mode ) {
168 case 'button' :
169 connectMode.eventMask = gpm.DOWN | gpm.UP ;
170 break ;
171 case 'drag' :
172 connectMode.eventMask = gpm.DRAG | gpm.DOWN | gpm.UP ;
173 break ;
174 case 'motion' :
175 default :
176 connectMode.eventMask = gpm.MOVE | gpm.DRAG | gpm.DOWN | gpm.UP ;
177 break ;
178 }
179
180 var handler = Object.create( gpm.Handler.prototype ) ;
181
182
183 var tty = termkit.tty.getPath( options.stdin ) ;
184
185 //if ( ! tty.index ) { handler.emit( 'error' , new Error( 'Not a TTY' ) ) ; return ; }
186
187 //console.log( 'TTY:' , tty.index ) ;
188 connectMode.vc = tty.index || 0 ;
189
190 handler.socket = new net.Socket() ;
191 var gpmConnect = gpm.connectStructureBuffer( connectMode ) ;
192
193 handler.socket.connect( '/dev/gpmctl' , () => {
194 //console.log( 'Connected' ) ;
195 handler.socket.write( gpmConnect ) ;
196 } ) ;
197
198 // Re-emit event
199 handler.socket.on( 'error' , ( error ) => { handler.emit( 'error' , error ) ; handler.close() ; } ) ;
200 handler.socket.on( 'end' , () => { handler.emit( 'end' ) ; } ) ;
201 handler.socket.on( 'close' , () => { handler.emit( 'close' ) ; handler.close() ; } ) ;
202
203 handler.socket.on( 'data' , ( buffer ) => {
204
205 //console.log( 'data' , buffer.length , buffer , '\n' , eventStructure( buffer ) ) ;
206 var rawEvent = gpm.eventStructure( buffer ) ;
207
208 if ( options.raw ) { handler.emit( 'mouse' , rawEvent ) ; return ; }
209
210 var terminalKitEvent = gpm.raw2terminalKitEvent( rawEvent ) ;
211 handler.emit( 'mouse' , terminalKitEvent[ 0 ] , terminalKitEvent[ 1 ] ) ;
212 } ) ;
213
214 return handler ;
215} ;
216
217
218
219// End/Close the underlying connection
220gpm.Handler.prototype.close = function handlerClose() {
221 if ( this.socket ) {
222 this.socket.destroy() ;
223 this.socket = undefined ;
224 }
225} ;
226
227
228
229// Transform raw GPM event to terminal-kit event
230gpm.raw2terminalKitEvent = function raw2terminalKitEvent( event ) {
231 var name ;
232
233 var terminalKitEvent = {
234 shift: !! ( event.modifiers & 1 ) ,
235 //altGr: event.modifiers & 2 ? true : false , // terminal-kit do not use altGr
236 ctrl: !! ( event.modifiers & 4 ) ,
237 alt: !! ( event.modifiers & 8 ) ,
238 x: event.x ,
239 y: event.y
240 } ;
241
242 if ( event.eType & gpm.DOWN ) {
243 name = 'MOUSE_LEFT_BUTTON_PRESSED' ;
244 }
245 else if ( event.eType & gpm.UP ) {
246 name = 'MOUSE_LEFT_BUTTON_RELEASED' ;
247 }
248 else if ( event.eType & gpm.MOVE || event.eType & gpm.DRAG ) {
249 name = 'MOUSE_MOTION' ;
250 }
251 else {
252 name = 'MOUSE_UNKNOWN' ;
253 }
254
255 return [ name , terminalKitEvent ] ;
256} ;
257
258