Hi,
I started working on keybindings again for GeanyPy and I was hoping to get a little advice from the experts.
I've hacked together some code that allows the single GeanyPy plugin to create new GeanyPlugin structs so that it can add multiple keygroups for the single plugin. This actually seems to work fairly well considering how kludgey the implementation is (ie. copying structs from Geanys code into GeanyPy's code because they aren't exposed, etc.).
Now I have two remaining challenges to overcome. The first is that there doesn't seem to be a way to remove/delete a keygroup, that is to undo what plugin_set_keygroup does. The function I think I need is keybindings_free_group(), but it's not in the plugin API, and I can't "fake" it like I've done with other parts since it requires the `keybinding_groups`. So basically what's happening, is when a Python plugin is unloaded and is cleaned up, I free the "sub" GeanyPlugin and all it's memory. Now when you go into the Preferences->Keybindings you see just weird things where the plugin name/keybindings were, presumably since Geany is reading from freed/adjacent memory or some such.
So the first question is, is there a way to remove a plugin/keygroup from those registered with Geany? At this point, I don't really care if it's a kludgey solution, I just want it working :)
The second challenge I'm facing is coming up with a way for GeanyPy to manage connection of Python plugins keybindings and dispatching to them when a keybinding is pressed. What I've done so far is, as mentioned above, to create a phony GeanyPlugin/GeanyKeygroup for each plugin, and each plugin gets allotted KEYBINDINGS_MAX bindings using the plugin_set_keygroup() function. The problem I'm having now is how to associate the Python plugin (consider it a C struct if it helps) with the keybinding. It seems like it would be easier if there was possibility of passing a closure or whatever it's called when connecting to the key binding, but GeanyKeyCallback doesn't seem to support this. I've a tried and failed so far with a few different ideas. So, can anyone recommend a decent way of mapping which Python plugin connected to which keybinding and then call out to the plugin when the keybinding is activated?
One last thing since I'm already writing a really long email, I notice in keybindings.h[1] in the GeanyKeyGroup definition, there's a 'plugin' member that is mentioned elsewhere in keybindings.c (I think) as being used by GeanyLua or some such. What is this for, and would it be useful for what I'm trying to do with GeanyPy?
Also, why is the comment above the definition "Plugins should not set these fields", while the comment for the 'plugin' member says "Used by plugins"?
[1] http://git.geany.org/geany/tree/src/keybindings.h#n58
Apologies for the really long message, but I'm at a bit of a loss (yet again).
Cheers, Matthew Brush
Le 12/09/2011 22:16, Matthew Brush a écrit :
Hi,
Hey,
I started working on keybindings again for GeanyPy and I was hoping to get a little advice from the experts.
Experts? OK, I don't feel concerned :D
I've hacked together some code that allows the single GeanyPy plugin to create new GeanyPlugin structs so that it can add multiple keygroups for the single plugin. This actually seems to work fairly well considering how kludgey the implementation is (ie. copying structs from Geanys code into GeanyPy's code because they aren't exposed, etc.).
Ouch
Now I have two remaining challenges to overcome. The first is that there doesn't seem to be a way to remove/delete a keygroup, that is to undo what plugin_set_keygroup does. The function I think I need is keybindings_free_group(), but it's not in the plugin API, and I can't "fake" it like I've done with other parts since it requires the `keybinding_groups`. So basically what's happening, is when a Python plugin is unloaded and is cleaned up, I free the "sub" GeanyPlugin and all it's memory. Now when you go into the Preferences->Keybindings you see just weird things where the plugin name/keybindings were, presumably since Geany is reading from freed/adjacent memory or some such.
So the first question is, is there a way to remove a plugin/keygroup from those registered with Geany? At this point, I don't really care if it's a kludgey solution, I just want it working :)
Unfortunately I think (as said on IRC) that the answer is "no". Unloading the plugin does it, but heh, you don't want to...
Actually this is related to hacking the GeanyPlugin stuff since normal plugins would break everything if they freed the keybindings themselves. Actually you'd need plugin_free(), but it's obviously not meant to be exported...
The second challenge I'm facing is coming up with a way for GeanyPy to manage connection of Python plugins keybindings and dispatching to them when a keybinding is pressed. What I've done so far is, as mentioned above, to create a phony GeanyPlugin/GeanyKeygroup for each plugin, and each plugin gets allotted KEYBINDINGS_MAX bindings using the plugin_set_keygroup() function. The problem I'm having now is how to associate the Python plugin (consider it a C struct if it helps) with the keybinding. It seems like it would be easier if there was possibility of passing a closure or whatever it's called when connecting to the key binding, but GeanyKeyCallback doesn't seem to support this. I've a tried and failed so far with a few different ideas. So, can anyone recommend a decent way of mapping which Python plugin connected to which keybinding and then call out to the plugin when the keybinding is activated?
Currently the keybinding's callback only have one parameter: the ID with which this keybinding was registered. We could imagine we add additional infos at some point for it to look like
void GeanyKeyGroupCallback (GeanyPlugin *plugin, guint key_id, gpointer user_data)
or something. Though, in most cases (e.g. "normal" plugins) the plugin argument is suite useless, and the user_data only allows to pass data without a global, so for most plugins (again) it only allows another style. In your case it would allow to register a kind of dispatch function and know to which "subplugin" dispatch it.
One last thing since I'm already writing a really long email, I notice in keybindings.h[1] in the GeanyKeyGroup definition, there's a 'plugin' member that is mentioned elsewhere in keybindings.c (I think) as being used by GeanyLua or some such. What is this for, and would it be useful for what I'm trying to do with GeanyPy?
Also, why is the comment above the definition "Plugins should not set these fields", while the comment for the 'plugin' member says "Used by plugins"?
The "plugin" member is a boolean that is used to know whether the key group is an internal one or a plugin one.internal.
Not sure what's the matter about GeanyLua, but it only speaks about duplicating string used as keybinding entries' name and label.
Regards, Colomban
Hi Matthew,
On 13 September 2011 06:16, Matthew Brush mbrush@codebrainz.ca wrote:
Hi,
I started working on keybindings again for GeanyPy and I was hoping to get a little advice from the experts.
Can't provide any of that, but can heckle from the cheap seats :)
I've hacked together some code that allows the single GeanyPy plugin to create new GeanyPlugin structs so that it can add multiple keygroups for the single plugin. This actually seems to work fairly well considering how kludgey the implementation is (ie. copying structs from Geanys code into GeanyPy's code because they aren't exposed, etc.).
After 0.21 release extensions of the plugin interface can be considered of course.
Now I have two remaining challenges to overcome. The first is that there doesn't seem to be a way to remove/delete a keygroup, that is to undo what plugin_set_keygroup does. The function I think I need is keybindings_free_group(), but it's not in the plugin API, and I can't "fake" it like I've done with other parts since it requires the `keybinding_groups`. So basically what's happening, is when a Python plugin is unloaded and is cleaned up, I free the "sub" GeanyPlugin and all it's memory. Now when you go into the Preferences->Keybindings you see just weird things where the plugin name/keybindings were, presumably since Geany is reading from freed/adjacent memory or some such.
ATM plugin keygroups are unloaded automagically on plugin free. We need to keep that behavior or break all other plugins.
My suggestion is that plugins.c be extended so that each plugin can have a list of keygroups (ie priv->key_group is a gslist). Unloading the plugin should free all the keygroups in the list.
Then add to pluginutils a function to remove a keygroup from that list (and which calls keybindings_free_group() in the background). Geanypy can call that when a "sub-plugin" goes away.
So the first question is, is there a way to remove a plugin/keygroup from those registered with Geany? At this point, I don't really care if it's a kludgey solution, I just want it working :)
Tut, tut, surely you want to find the elegant recursive provably correct functional solution that runs in treacle :-)
The second challenge I'm facing is coming up with a way for GeanyPy to manage connection of Python plugins keybindings and dispatching to them when a keybinding is pressed. What I've done so far is, as mentioned above, to create a phony GeanyPlugin/GeanyKeygroup for each plugin, and each plugin gets allotted KEYBINDINGS_MAX bindings using the plugin_set_keygroup() function. The problem I'm having now is how to associate the Python plugin (consider it a C struct if it helps) with the keybinding. It seems like it would be easier if there was possibility of passing a closure or whatever it's called when connecting to the key binding, but GeanyKeyCallback doesn't seem to support this. I've a tried and failed so far with a few different ideas. So, can anyone recommend a decent way of mapping which Python plugin connected to which keybinding and then call out to the plugin when the keybinding is activated?
Presumably you know how to call back into the interpretor (if not I suggest looking at how PyGTK does it) so its just a matter of mapping from the key_id to a Python callable, I'd have all keys call one Python method that just looks up a callable in a dict by key_id. Clearly that means you don't expose the Geany's set key but a new Python function that records the key in the dict and calls Geany's set key.
One last thing since I'm already writing a really long email, I notice in keybindings.h[1] in the GeanyKeyGroup definition, there's a 'plugin' member that is mentioned elsewhere in keybindings.c (I think) as being used by GeanyLua or some such. What is this for, and would it be useful for what I'm trying to do with GeanyPy?
No just distinguishes between native and plugin keybindings. Dunno what the Lua references are. Note that IIUC GeanyLua doesn't do any of what you are trying to do so its not surprising that you need new functionality in the plugin interface.
Also, why is the comment above the definition "Plugins should not set these fields", while the comment for the 'plugin' member says "Used by plugins"?
Comment means this keybinding is "Used by plugin" if it is true.
[1] http://git.geany.org/geany/tree/src/keybindings.h#n58
Apologies for the really long message, but I'm at a bit of a loss (yet again).
Not lost, just geographically embarrassed :)
Cheers Lex
On 09/12/2011 04:37 PM, Lex Trotman wrote:
Hi Matthew,
On 13 September 2011 06:16, Matthew Brushmbrush@codebrainz.ca wrote:
Hi,
I started working on keybindings again for GeanyPy and I was hoping to get a little advice from the experts.
Can't provide any of that, but can heckle from the cheap seats :)
Thanks for the info, and same to Colomban.
[...]
Presumably you know how to call back into the interpretor (if not I suggest looking at how PyGTK does it) so its just a matter of mapping from the key_id to a Python callable, I'd have all keys call one Python method that just looks up a callable in a dict by key_id. Clearly that means you don't expose the Geany's set key but a new Python function that records the key in the dict and calls Geany's set key.
Here's what I'm thinking of going with for now: https://github.com/codebrainz/geanypy/blob/keybindings/src/bindings.c
I think it will be sufficient for now. What it does is adds a `bindings` module in the `geany` package which Python plugins can use to register keybindings, so in practice it looks like this in the plugin's code:
import geany
class MyPlugin(geany.Plugin):
def __init__(self): geany.Plugin.__init__(self) # register keybinding geany.bindings.register_binding( 'MyPlugin', # name of the plugin 'Do stuff', # name of the binding self.on_kb_activate, # callback 'Some user data') # user data
def on_kb_activate(self, keyid, data): print 'Got %d: %s' % (keyid, data)
In the preferences dialog, all Python bindings show up under the 'GeanyPy' keygroup and their labels are formatted with the plugin name and the keybinding label (ex. "MyPlugin: Do stuff").
Of course it needs more thought and lots more work, but I guess I will have to settle for something like this until we get motivated to mess with the plugin code in Geany.
At least it's not nearly as hacky as my previous ideas :)
Cheers, Matthew Brush
[...]
Of course it needs more thought and lots more work, but I guess I will have to settle for something like this until we get motivated to mess with the plugin code in Geany.
At least it's not nearly as hacky as my previous ideas :)
Yeah, looks like a viable interim workaround.
Cheers Lex