11const Signals = imports . signals ;
22const Main = imports . ui . main ;
33const Gio = imports . gi . Gio ;
4+ const GLib = imports . gi . GLib ;
45const Lang = imports . lang ;
56const Util = imports . misc . util ;
67const Meta = imports . gi . Meta ;
8+ const AppletManager = imports . ui . appletManager ;
9+ const DeskletManager = imports . ui . deskletManager ;
710
811const MK = imports . gi . CDesktopEnums . MediaKeyType ;
912const CinnamonDesktop = imports . gi . CinnamonDesktop ;
@@ -52,6 +55,7 @@ KeybindingManager.prototype = {
5255 * This dict will contain [name, bindings, callback] and keyed on the id returned by
5356 * add_custom_keybinding. */
5457 this . bindings = new Map ( ) ;
58+ this . applet_bindings = new Map ( ) ;
5559 this . kb_schema = Gio . Settings . new ( CUSTOM_KEYS_PARENT_SCHEMA ) ;
5660 this . setup_custom_keybindings ( ) ;
5761 this . kb_schema . connect ( "changed::custom-list" , Lang . bind ( this , this . on_customs_changed ) ) ;
@@ -72,6 +76,171 @@ KeybindingManager.prototype = {
7276 return this . addHotKeyArray ( name , bindings_string . split ( "::" ) , callback ) ;
7377 } ,
7478
79+ _makeXletKey : function ( xlet , name , binding ) {
80+ return `${ xlet . _uuid } ::${ name } ::${ binding } ` ;
81+ } ,
82+
83+ _uuidFromXletKey : function ( xlet_key ) {
84+ return xlet_key . split ( "::" ) [ 0 ] ;
85+ } ,
86+
87+ /* Menu applet example
88+ *
89+ * uuid: menu@cinnamon.org
90+ * binding name: overlay-key
91+ * instances:
92+ * 49: super-l, super-r
93+ * 52: super-l, ctrl-shift-f7
94+ *
95+ * is in applet_bindings as:
96+ *
97+ * {
98+ * "menu@cinnamon .org::overlay-key:super-l" : {
99+ * "49": callback49,
100+ * "52": callback52
101+ * },
102+ * "menu@cinnamon.org::overlay-key:super-r" : {
103+ * "49": callback49
104+ * },
105+ * "menu@cinnamon.org::overlay-key:ctrl-shift-f7" : {
106+ * "52": callback52
107+ * }
108+ * }
109+ */
110+
111+ addXletHotKey : function ( xlet , name , bindings_string , callback ) {
112+ this . _removeMatchingXletBindings ( xlet , name ) ;
113+
114+ if ( ! bindings_string )
115+ return false ;
116+
117+ let xlet_set = null ;
118+ const instanceId = xlet . instance_id || 0 ; // extensions == undefined
119+ const binding_array = bindings_string . split ( "::" ) ;
120+
121+ for ( const binding of binding_array ) {
122+ const xlet_key = this . _makeXletKey ( xlet , name , binding ) ;
123+ xlet_set = this . applet_bindings . get ( xlet_key ) ;
124+
125+ if ( xlet_set === undefined ) {
126+ xlet_set = new Map ( [
127+ [ "commitTimeoutId" , 0 ]
128+ ] ) ;
129+ this . applet_bindings . set ( xlet_key , xlet_set ) ;
130+ }
131+
132+ xlet_set . set ( instanceId , callback ) ;
133+
134+ this . _queueCommitXletHotKey ( xlet_key , binding , xlet_set ) ;
135+ }
136+ } ,
137+
138+ _removeMatchingXletBindings : function ( xlet , name ) {
139+ // This sucks, but since the individual binding string is part of the name
140+ // name we send to muffin, we can't just call display.remove_keybinding(name),
141+ // and need to iterate thru the list finding our matching uuid and instance ids.
142+ const key_prefix = `${ xlet . _uuid } ::${ name } ::` ;
143+ const instanceId = xlet . instance_id || 0 ;
144+ const iter = this . applet_bindings . keys ( ) ;
145+
146+ for ( const xlet_key of iter ) {
147+ if ( xlet_key . startsWith ( key_prefix ) ) {
148+ const xlet_set = this . applet_bindings . get ( xlet_key ) ;
149+ if ( xlet_set . has ( instanceId ) ) {
150+ xlet_set . delete ( instanceId ) ;
151+ if ( xlet_set . size === 1 ) { // only commitTimeoutId left
152+ this . applet_bindings . delete ( xlet_key ) ;
153+ this . removeHotKey ( xlet_key ) ;
154+ }
155+ }
156+ }
157+ }
158+ } ,
159+
160+ _xletCallback : function ( lookup_key , display , window , kb , action ) {
161+ const xlet_set = this . applet_bindings . get ( lookup_key ) ;
162+ if ( ! xlet_set ) {
163+ return ;
164+ }
165+
166+ /* This should catch extensions also. The minimum size is 2 - 1 will be a
167+ * binding, plus the commitTimeoutId. */
168+ if ( xlet_set . size === 2 ) {
169+ const iter = xlet_set . keys ( ) ;
170+ for ( const instanceId of iter ) {
171+ if ( instanceId === "commitTimeoutId" ) {
172+ continue ;
173+ }
174+ const callback = xlet_set . get ( instanceId ) ;
175+ callback ( display , window , kb , action ) ;
176+ break ;
177+ }
178+ return ;
179+ }
180+
181+ const iter = xlet_set . keys ( ) ;
182+ const uuid = this . _uuidFromXletKey ( lookup_key ) ;
183+ const currentMonitor = Main . layoutManager . currentMonitor . index ;
184+
185+ let xlet = null
186+ let xletMonitor = 0 ;
187+ let primary_callback = null ;
188+ let current_callback = null ;
189+
190+ for ( instanceId of iter ) {
191+ current_callback = xlet_set . get ( instanceId ) ;
192+
193+ xlet = AppletManager . get_object_for_uuid ( uuid , instanceId ) ;
194+
195+ if ( ! xlet ) {
196+ xlet = DeskletManager . get_object_for_uuid ( uuid , instanceId ) ;
197+ }
198+
199+ if ( xlet ) {
200+ const actor = xlet . actor ;
201+ if ( actor ) {
202+ xletMonitor = Main . layoutManager . findMonitorIndexForActor ( actor ) ;
203+
204+ if ( xletMonitor === Main . layoutManager . primaryMonitor . index ) {
205+ primary_callback = current_callback ;
206+ }
207+ if ( xletMonitor == currentMonitor ) {
208+ current_callback ( display , window , kb , action ) ;
209+ return ;
210+ }
211+ }
212+ }
213+ }
214+
215+ // No match... more monitors than instances? Prefer the primary monitor's if we encountered it.
216+ if ( primary_callback ) {
217+ primary_callback ( display , window , kb , action ) ;
218+ } else {
219+ // Fallback to the last one we looked at otherwise.
220+ current_callback ( display , window , kb , action ) ;
221+ }
222+ } ,
223+
224+ removeXletHotKey : function ( xlet , name ) {
225+ this . _removeMatchingXletBindings ( xlet , name ) ;
226+ } ,
227+
228+ _queueCommitXletHotKey : function ( xlet_key , binding , xlet_set ) {
229+ let id = xlet_set . get ( "commitTimeoutId" ) ?? 0 ;
230+
231+ if ( id > 0 ) {
232+ GLib . source_remove ( id ) ;
233+ }
234+
235+ id = GLib . idle_add ( GLib . PRIORITY_DEFAULT , ( ) => {
236+ this . addHotKeyArray ( xlet_key , [ binding ] , this . _xletCallback . bind ( this , xlet_key ) ) ;
237+ xlet_set . set ( "commitTimeoutId" , 0 ) ;
238+ return GLib . SOURCE_REMOVE ;
239+ } ) ;
240+
241+ xlet_set . set ( "commitTimeoutId" , id ) ;
242+ } ,
243+
75244 _lookupEntry : function ( name ) {
76245 let found = 0 ;
77246 for ( const action_id of this . bindings . keys ( ) ) {
0 commit comments