From arvidb at kth.se Wed Oct 23 19:58:25 2013 From: arvidb at kth.se (Arvid Brodin) Date: Thu, 24 Oct 2013 02:58:25 +0200 Subject: [crossfire] Key repeat and keybindings Message-ID: <526870B1.5060201@kth.se> I recently started playing Crossfire (on a private server, together with my girlfriend, if you can believe it! :). It's a surprisingly addictive game! And we're slowly finding nice features like auto-pickup and -mapscale. So thanks! :) There are a few things that we are both annoyed with though. One is the key press handling. If I "walk" by holding a direction key, it seems key repeats are queued so that the PC continues to walk long after the button is released. Usually into a trapped door... Similarly with search: hold down the search key to make a thorough search of the immediate area (say, 4 x 5 searches), and after the key is released, and additional 2 x 5 searches or so are made before the PC can do anything else. The delay is a couple of seconds and makes the client feel really sluggish. This usually happen when something horrible shows up, like a "sweet little girl". (She killed me. Oh, the shame!) Is there a reason not all buttons are "level-triggered" during non-text-entry? I've taken a look at the code, and it seems the server handles run/fire differently than other commands? Also, it would be great to split run into attack and run, so that running into doors does not make one sick (and only trying to open them with "attack" does). (And I accidentally slaughtered a cleaning lady the other day for the same reason.) I.e. only direction key == move (and move only) while pressed. And CTRL modifier for attack, SHIFT for fire, as today. Another somewhat related thing that really confused me in the beginning are the built-in keybindings. These do not show up in the Keybindings dialog. Somewhere in one of the beginner's maps, I think I was told to bind "use_skill find traps" to something - and I naturally chose 's'. And was told: "Warning: Keybind search may conflict with new binding." OK, now just what does that mean?? (I understand it now, but as a complete beginner that was just weird. Hmm, it's still weird - why have two commands for the same thing?) Why not just use default keybindings instead of the built-ins (i.e. if no keybindings file was found)? This would mess things up a bit for people upgrading their client, but should be easily fixed by binding 'a', 's', 'd' etc. Maybe I can take a look at these things. Would there be interest in patches, or are there reasons things are like they are? -- Arvid From davidnicholashurst at gmail.com Wed Oct 23 20:53:59 2013 From: davidnicholashurst at gmail.com (David Hurst) Date: Thu, 24 Oct 2013 12:53:59 +1100 Subject: [crossfire] Key repeat and keybindings In-Reply-To: <526870B1.5060201@kth.se> References: <526870B1.5060201@kth.se> Message-ID: Hi Arvid, I have discussed these issues in some detail in the IRC channel. I believe I can answer your questions and propose solutions, sadly I lack the talent to make any changes. > There are a few things that we are both annoyed with though. One is the key press handling. If I "walk" by holding a direction key, it seems key repeats are queued so that the PC continues to walk long after the button is released. Usually into a trapped door... > > Similarly with search: hold down the search key to make a thorough search of the immediate area (say, 4 x 5 searches), and after the key is released, and additional 2 x 5 searches or so are made before the PC can do anything else. The delay is a couple of seconds and makes the client feel really sluggish. This usually happen when something horrible shows up, like a "sweet little girl". (She killed me. Oh, the shame!) This is client side effect. The client will store up to 10 commands before 'overflowing' and ignoring further commands. For very slow actions this is very noticeable. When a player starts running the client will continue to send running events (i think this is actualy server side, sorry beyond my knowledge) until an interrupt is sent to stop. The solution I have discussed was that the client should queue commands as long as they are the same action as per the current system, however, if an alternate action is pressed it should interrupt the queue and immediately do this action instead. I believe it was added to someones to do list right down the bottom but I agree that this seems to be a really powerful aspect of crossfire which adds a laggy or delayed feel to an otherwise rapid paced game. I suggested this might be a client setting that users can set a preference for. Basically, yes I agree with your concerns and questions that this aspect of crossfire could be improved and I believe the current bottleneck is simply time. The age old #crossfire response: "sure, code it". In the short term I found it very valuable to understand how the client works, I have changed the way I make key pressed to minimize such "sweet little girl" scenarios as you have described :). Regards, dnh On Thu, Oct 24, 2013 at 11:58 AM, Arvid Brodin wrote: > I recently started playing Crossfire (on a private server, together with > my girlfriend, if you can believe it! :). > > It's a surprisingly addictive game! And we're slowly finding nice features > like auto-pickup and -mapscale. So thanks! :) > > > There are a few things that we are both annoyed with though. One is the > key press handling. If I "walk" by holding a direction key, it seems key > repeats are queued so that the PC continues to walk long after the button > is released. Usually into a trapped door... > > Similarly with search: hold down the search key to make a thorough search > of the immediate area (say, 4 x 5 searches), and after the key is released, > and additional 2 x 5 searches or so are made before the PC can do anything > else. The delay is a couple of seconds and makes the client feel really > sluggish. This usually happen when something horrible shows up, like a > "sweet little girl". (She killed me. Oh, the shame!) > > Is there a reason not all buttons are "level-triggered" during > non-text-entry? I've taken a look at the code, and it seems the server > handles run/fire differently than other commands? > > Also, it would be great to split run into attack and run, so that running > into doors does not make one sick (and only trying to open them with > "attack" does). (And I accidentally slaughtered a cleaning lady the other > day for the same reason.) I.e. only direction key == move (and move only) > while pressed. And CTRL modifier for attack, SHIFT for fire, as today. > > > Another somewhat related thing that really confused me in the beginning > are the built-in keybindings. These do not show up in the Keybindings > dialog. Somewhere in one of the beginner's maps, I think I was told to bind > "use_skill find traps" to something - and I naturally chose 's'. And was > told: > > "Warning: Keybind search may conflict with new binding." > > OK, now just what does that mean?? (I understand it now, but as a complete > beginner that was just weird. Hmm, it's still weird - why have two commands > for the same thing?) Why not just use default keybindings instead of the > built-ins (i.e. if no keybindings file was found)? This would mess things > up a bit for people upgrading their client, but should be easily fixed by > binding 'a', 's', 'd' etc. > > > > Maybe I can take a look at these things. Would there be interest in > patches, or are there reasons things are like they are? > > > -- > Arvid > _______________________________________________ > crossfire mailing list > crossfire at metalforge.org > http://mailman.metalforge.org/mailman/listinfo/crossfire > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mwedel at sonic.net Thu Oct 24 00:19:12 2013 From: mwedel at sonic.net (Mark Wedel) Date: Wed, 23 Oct 2013 22:19:12 -0700 Subject: [crossfire] Key repeat and keybindings In-Reply-To: References: <526870B1.5060201@kth.se> Message-ID: <5268ADD0.3080107@sonic.net> On 10/23/13 06:53 PM, David Hurst wrote: > Hi Arvid, > > I have discussed these issues in some detail in the IRC channel. I believe I can > answer your questions and propose solutions, sadly I lack the talent to make any > changes. > >> There are a few things that we are both annoyed with though. One is the key > press handling. If I "walk" by holding a direction key, it seems key repeats are > queued so that the PC continues to walk long after the button is released. > Usually into a trapped door... > > >> Similarly with search: hold down the search key to make a thorough search of > the immediate area (say, 4 x 5 searches), and after the key is released, and > additional 2 x 5 searches or so are made before the PC can do anything else. The > delay is a couple of seconds and makes the client feel really sluggish. This > usually happen when something horrible shows up, like a "sweet little girl". > (She killed me. Oh, the shame!) > > This is client side effect. The client will store up to 10 commands before > 'overflowing' and ignoring further commands. For very slow actions this is very > noticeable. When a player starts running the client will continue to send > running events (i think this is actualy server side, sorry beyond my knowledge) > until an interrupt is sent to stop. The solution I have discussed was that the > client should queue commands as long as they are the same action as per the > current system, however, if an alternate action is pressed it should interrupt > the queue and immediately do this action instead. I believe it was added to > someones to do list right down the bottom but I agree that this seems to be a > really powerful aspect of crossfire which adds a laggy or delayed feel to an > otherwise rapid paced game. I suggested this might be a client setting that > users can set a preference for. A lot of this has to down how keys are handled, at least on x-windows. If you could down the control, shift, or other modifier keys, a single event is generated - that the key has been pressed, and another event is generated when the key is released. This is all done in the x-server - at a level beyond the client. For other keys, autorepeat kicks in. So if you hold an arrow key, the x-server is generating numerous key press/key release events, which the client then sees and takes as different key presses. Best I know, there isn't an easy way to know if multiple key presses are a result of autorepeat or if the player is actually pressing and releasing the key (as a side note, for myself, for things like search, I would just hit the s key 5 times to know how much I'm searching) Given this background, run and fire are handled differently, since these are modifiers - when the client gets that the key is pressed, it just sends the appropriate fire_on or run_on to the server, and when key is release, send fire_off/run_off. As you can imagine, for other keys, it can't use this same approach - the arrow key will generate constant press/release events - while the client can ignore them, that probably isn't desirable either. You can actually change the amount of commands the server/client buffer - this is in the config window - if you are playing on a lan, dropping it down to 1 or 2 is probably fine. The reason that might not be good on WANS is that if the server is far away, the character may end up not doing anything as it hasn't receive commands for it because of the time it takes for the data to go back and forth. Another complication in all this is that how many actions the character gets isn't constant - it may move slower if it has a lot of equipment on, could move faster with different spells, etc, so it is a little harder to keep it all in sync. > > Basically, yes I agree with your concerns and questions that this aspect of > crossfire could be improved and I believe the current bottleneck is simply time. > The age old #crossfire response: "sure, code it". Yep - and given this isn't just a simple fix, such code is more difficult. The proposal to fix it would be for the server to inspect all commands from the client as it gets them. Right now it is a FIFO - if the server inspects them, certain commands (drink healing potion) could have higher priority than other commands. This also allows something like 'ignore all commands up to this command' to basically flush the queue - in that way, one could interrupt things. However, when to interrupt and when not still isn't an easy problem. It may simple enough - if I'm holding the north key down, then when I press the east key, have it drop any queued up north commands and just start moving east. But suppose I'm on a map (saw a town) and know I want to go 5 north and 3 east, and press those keys individually - as noted above, there is no easy way for the client to know that is the case vs keys being held down. > > In the short term I found it very valuable to understand how the client works, I > have changed the way I make key pressed to minimize such "sweet little girl" > scenarios as you have described :). Yes - exactly how it all works likely will help determine the sweet spot. From arvidb at kth.se Fri Oct 25 18:59:25 2013 From: arvidb at kth.se (Arvid Brodin) Date: Sat, 26 Oct 2013 01:59:25 +0200 Subject: [crossfire] Key repeat and keybindings In-Reply-To: <5268ADD0.3080107@sonic.net> References: <526870B1.5060201@kth.se> <5268ADD0.3080107@sonic.net> Message-ID: <526B05DD.6070304@kth.se> On 2013-10-24 07:19, Mark Wedel wrote: > On 10/23/13 06:53 PM, David Hurst wrote: >> Hi Arvid, Hi! [] > > A lot of this has to down how keys are handled, at least on > x-windows. > > If you could down the control, shift, or other modifier keys, a > single event is generated - that the key has been pressed, and > another event is generated when the key is released. This is all > done in the x-server - at a level beyond the client. > > For other keys, autorepeat kicks in. So if you hold an arrow key, > the x-server is generating numerous key press/key release events, > which the client then sees and takes as different key presses. Best > I know, there isn't an easy way to know if multiple key presses are a > result of autorepeat or if the player is actually pressing and > releasing the key (as a side note, for myself, for things like > search, I would just hit the s key 5 times to know how much I'm > searching) I did a test of this. In the client key press events are handled by keyfunc() and key releases are handled by keyrelfunc() (both in gtk-v2/src/keys.c). They are not x-server events but rather GDK events. I added a simple printf() at the beginning of each to see what kind of events are generated for different keys. For Ctrl, the result is as you say: key pressed: Control_L key released: Control_L For auto-repeating keys though, there are no release events between the key press events: key pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key released: KP_2 So keeping state for each button would be easy. It's also easy to see if a key is actually pressed several times vs being held down. (Maybe focus in/out can make things a bit more difficult, but not a lot, I guess.) > Given this background, run and fire are handled differently, since > these are modifiers - when the client gets that the key is pressed, > it just sends the appropriate fire_on or run_on to the server, and > when key is release, send fire_off/run_off. And here of course lies the problem: which commands should be of the type start/stop, and which should be of the type "do once"? It would be pretty bad if one sent a command to drink a potion and the PC drank _all_ potions of the type because the key was held too long. But other commands - search, disarm, run, maybe fire? - may be better suited as start/stop? It could be as simple as adding a few new start/stop type commands to the server - walk (without attacking), search, and disarm, to begin with? - and updating the client to use them. Assuming the client key handling is in place, of course. ---- I've done some coding on the other, related issue of key combinations/modifiers and key bindings. Today the client is extremely flexible: you can actually re-bind the run, fire, alt and meta modifier keys! Is this a popular feature? Also, it is unclear how combinations of modifiers work: if you check both Run and Fire in the Keybindings dialog, does the key work only when they are both depressed, or with any of them? If you select no modifiers, does the key then work regardless of modifier keys or only when no modifier keys are held? The code is a bit ambivalent on this, it seems (maybe this works systematically by chance, but I found at least one obvious mistake in the code, as well as code comments leading me to this conclusion). I reworked the code a bit and added an "Any" checkbox in the Keybindings dialog. This way you can choose whether the key should ignore modifiers, or set the same key to different commands depending on the combination of modifier keys held. At the same time I removed the code that treats default key bindings specially, so now you can easily unbind/change them in the dialog box or using the unbind command. (You could do this via the "unbind -g" command before, but how would you know without looking at the code?) I also removed the possibility to re-bind the Ctrl, Shift, Alt and Meta keys since that just seems overly complex. I can post the patch if anyone is interested. It also contains quite a few style fixes though (mostly remove space before function '(', space after comma, space around '='/'==' etc). I tried to use the most prevalent style already in use in the file, but I should probably have done it in a separate patch... -- Arvid From mwedel at sonic.net Sat Oct 26 01:21:00 2013 From: mwedel at sonic.net (Mark Wedel) Date: Fri, 25 Oct 2013 23:21:00 -0700 Subject: [crossfire] Key repeat and keybindings In-Reply-To: <526B05DD.6070304@kth.se> References: <526870B1.5060201@kth.se> <5268ADD0.3080107@sonic.net> <526B05DD.6070304@kth.se> Message-ID: <526B5F4C.5040001@sonic.net> On 10/25/13 04:59 PM, Arvid Brodin wrote: > On 2013-10-24 07:19, Mark Wedel wrote: >> On 10/23/13 06:53 PM, David Hurst wrote: >>> Hi Arvid, > > Hi! > > [] >> >> A lot of this has to down how keys are handled, at least on x-windows. >> >> If you could down the control, shift, or other modifier keys, a single >> event is generated - that the key has been pressed, and another event is >> generated when the key is released. This is all done in the x-server - at >> a level beyond the client. >> >> For other keys, autorepeat kicks in. So if you hold an arrow key, the >> x-server is generating numerous key press/key release events, which the >> client then sees and takes as different key presses. Best I know, there >> isn't an easy way to know if multiple key presses are a result of >> autorepeat or if the player is actually pressing and releasing the key (as >> a side note, for myself, for things like search, I would just hit the s key >> 5 times to know how much I'm searching) > > I did a test of this. In the client key press events are handled by keyfunc() > and key releases are handled by keyrelfunc() (both in gtk-v2/src/keys.c). > They are not x-server events but rather GDK events. I added a simple printf() > at the beginning of each to see what kind of events are generated for > different keys. > > For Ctrl, the result is as you say: > > key pressed: Control_L key released: > Control_L > > For auto-repeating keys though, there are no release events between the key > press events: > > key pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key > pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key released: KP_2 > > So keeping state for each button would be easy. It's also easy to see if a > key is actually pressed several times vs being held down. (Maybe focus in/out > can make things a bit more difficult, but not a lot, I guess.) > Interesting. So I'm guessing that gtk must capture some of the events. If I run xev, I do see key press/key release events for the keys. But even in the above example, it potentially means a more complicated setup, in that the client (or something) has to keep track if the release events has occurred. Note that focus can mess things up. Start running and then have the client lose focus - your character will continue to run, even after you release the keys. However, this is more a special case I'm not too concerned with. > >> Given this background, run and fire are handled differently, since these >> are modifiers - when the client gets that the key is pressed, it just sends >> the appropriate fire_on or run_on to the server, and when key is release, >> send fire_off/run_off. > > And here of course lies the problem: which commands should be of the type > start/stop, and which should be of the type "do once"? It would be pretty bad > if one sent a command to drink a potion and the PC drank _all_ potions of the > type because the key was held too long. But other commands - search, disarm, > run, maybe fire? - may be better suited as start/stop? > > It could be as simple as adding a few new start/stop type commands to the > server - walk (without attacking), search, and disarm, to begin with? - and > updating the client to use them. Assuming the client key handling is in > place, of course. I'd really prefer this not be in the server, and instead be in the client. the fire_on and run_on are really pretty terrible hacks as is, and I'd rather not add more of them. The protocol has improved since those were put in. The client should be able to better watch the number of acknowledge commands and send or not send commands as appropriate. Note per my previous mail, the next enhancement that would be nice would be for the server to look at all commands, and in addition to having priority, there could be something like 'cancel are north commands', such that when the key is released, that is sent to the server and the server does just that. That said, even this gets tricky - you don't want to cancel the a single command that was sent, which might mean the client has to know if multiple commands have been sent, and which ones. This reduces the server having to remember more state. In terms of what commands should repeat and what should not, this could probably be an option in the keybind menu, eg, 'repeat?' which notes if the command should be repeated or not. Probably also allow it in the keybindings itself, because for some commands, there may be desire to repeat them in certain circumstances and not other (apply immediately comes to mind in certain cases, like eating a lot of low value food, but not in others, like using exits) > > > ---- > > I've done some coding on the other, related issue of key > combinations/modifiers and key bindings. > > Today the client is extremely flexible: you can actually re-bind the run, > fire, alt and meta modifier keys! Is this a popular feature? Not sure, although I could imagine depending on keyboard layout, etc, there may be desire to do so. > > Also, it is unclear how combinations of modifiers work: if you check both Run > and Fire in the Keybindings dialog, does the key work only when they are both > depressed, or with any of them? If you select no modifiers, does the key then > work regardless of modifier keys or only when no modifier keys are held? > > The code is a bit ambivalent on this, it seems (maybe this works > systematically by chance, but I found at least one obvious mistake in the > code, as well as code comments leading me to this conclusion). > > > I reworked the code a bit and added an "Any" checkbox in the Keybindings > dialog. This way you can choose whether the key should ignore modifiers, or > set the same key to different commands depending on the combination of > modifier keys held. Yes, that seems like a good fix. > > At the same time I removed the code that treats default key bindings > specially, so now you can easily unbind/change them in the dialog box or > using the unbind command. (You could do this via the "unbind -g" command > before, but how would you know without looking at the code?) > > I also removed the possibility to re-bind the Ctrl, Shift, Alt and Meta keys > since that just seems overly complex. > > I can post the patch if anyone is interested. It also contains quite a few > style fixes though (mostly remove space before function '(', space after > comma, space around '='/'==' etc). I tried to use the most prevalent style > already in use in the file, but I should probably have done it in a separate > patch... I'd certainly say it is worth it to post it or upload it to sourceforge on the tracker. From arvidb at kth.se Sun Oct 27 09:43:16 2013 From: arvidb at kth.se (Arvid Brodin) Date: Sun, 27 Oct 2013 15:43:16 +0100 Subject: [crossfire] Key repeat and keybindings In-Reply-To: <526B5F4C.5040001@sonic.net> References: <526870B1.5060201@kth.se> <5268ADD0.3080107@sonic.net> <526B05DD.6070304@kth.se> <526B5F4C.5040001@sonic.net> Message-ID: <526D2684.8090102@kth.se> On 2013-10-26 08:21, Mark Wedel wrote: > On 10/25/13 04:59 PM, Arvid Brodin wrote: >> On 2013-10-24 07:19, Mark Wedel wrote: >>> On 10/23/13 06:53 PM, David Hurst wrote: >>>> Hi Arvid, >> >> Hi! >> [] >> For auto-repeating keys though, there are no release events between the key >> press events: >> >> key pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key >> pressed: KP_2 key pressed: KP_2 key pressed: KP_2 key released: KP_2 >> >> So keeping state for each button would be easy. It's also easy to see if a >> key is actually pressed several times vs being held down. (Maybe focus in/out >> can make things a bit more difficult, but not a lot, I guess.) >> > > Interesting. So I'm guessing that gtk must capture some of the events. If I > run xev, I do see key press/key release events for the keys. > > But even in the above example, it potentially means a more complicated setup, > in that the client (or something) has to keep track if the release events has occurred. Well, we already have a struct in which we keep info for each interesting key, with quick, hashed access, so storing the state would be as easy as three lines of code. >> It could be as simple as adding a few new start/stop type commands to the >> server - walk (without attacking), search, and disarm, to begin with? - and >> updating the client to use them. Assuming the client key handling is in >> place, of course. > > I'd really prefer this not be in the server, and instead be in the client. the fire_on and run_on are really pretty terrible hacks as is, and I'd rather not add more of them. I haven't looked at the server code, but the client hack is a bit terrible, yes. :) > The protocol has improved since those were put in. The client should be able to better watch the number of acknowledge commands and send or not send commands as appropriate. Note per my previous mail, the next enhancement that would be nice would be for the server to look at all commands, and in addition to having priority, there could be something like 'cancel are north commands', such that when the key is released, that is sent to the server and the server does just that. That said, even this gets tricky - you don't want to cancel the a single command that was sent, which might mean the client has to know if multiple commands have been sent, and which ones. If the client waits for the acknowledgements before sending the repeat, isn't there a risk that we handicap players with a high network latency? If the acks are sent from the server immediately upon reception of the command, perhaps this wouldn't be so bad in most cases. If the ack is sent after the command has been executed, it would be a big problem. > This reduces the server having to remember more state. > > In terms of what commands should repeat and what should not, this could probably be an option in the keybind menu, eg, 'repeat?' which notes if the command should be repeated or not. Probably also allow it in the keybindings itself, because for some commands, there may be desire to repeat them in certain circumstances and not other (apply immediately comes to mind in certain cases, like eating a lot of low value food, but not in others, like using exits) This is a great idea! It would also solve the problem with deciding whether to cancel a queue of commands or not - only on releasing the key of a repeating command should a "reset queue" command be sent to the server. >> >> >> ---- >> >> I've done some coding on the other, related issue of key >> combinations/modifiers and key bindings. >> >> Today the client is extremely flexible: you can actually re-bind the run, >> fire, alt and meta modifier keys! Is this a popular feature? > > Not sure, although I could imagine depending on keyboard layout, etc, there may be desire to do so. > >> >> Also, it is unclear how combinations of modifiers work: if you check both Run >> and Fire in the Keybindings dialog, does the key work only when they are both >> depressed, or with any of them? If you select no modifiers, does the key then >> work regardless of modifier keys or only when no modifier keys are held? >> >> The code is a bit ambivalent on this, it seems (maybe this works >> systematically by chance, but I found at least one obvious mistake in the >> code, as well as code comments leading me to this conclusion). >> >> >> I reworked the code a bit and added an "Any" checkbox in the Keybindings >> dialog. This way you can choose whether the key should ignore modifiers, or >> set the same key to different commands depending on the combination of >> modifier keys held. > > Yes, that seems like a good fix. > >> >> At the same time I removed the code that treats default key bindings >> specially, so now you can easily unbind/change them in the dialog box or >> using the unbind command. (You could do this via the "unbind -g" command >> before, but how would you know without looking at the code?) >> >> I also removed the possibility to re-bind the Ctrl, Shift, Alt and Meta keys >> since that just seems overly complex. >> >> I can post the patch if anyone is interested. It also contains quite a few >> style fixes though (mostly remove space before function '(', space after >> comma, space around '='/'==' etc). I tried to use the most prevalent style >> already in use in the file, but I should probably have done it in a separate >> patch... > > I'd certainly say it is worth it to post it or upload it to sourceforge on the tracker. Which is the preferred method? I'm used to sending patches inline in emails; the sourceforge tracker is new to me. -- Arvid From mwedel at sonic.net Mon Oct 28 00:38:59 2013 From: mwedel at sonic.net (Mark Wedel) Date: Sun, 27 Oct 2013 22:38:59 -0700 Subject: [crossfire] Key repeat and keybindings In-Reply-To: <526D2684.8090102@kth.se> References: <526870B1.5060201@kth.se> <5268ADD0.3080107@sonic.net> <526B05DD.6070304@kth.se> <526B5F4C.5040001@sonic.net> <526D2684.8090102@kth.se> Message-ID: <526DF873.1090001@sonic.net> >> The protocol has improved since those were put in. The client should be >> able to better watch the number of acknowledge commands and send or not >> send commands as appropriate. Note per my previous mail, the next >> enhancement that would be nice would be for the server to look at all >> commands, and in addition to having priority, there could be something like >> 'cancel are north commands', such that when the key is released, that is >> sent to the server and the server does just that. That said, even this >> gets tricky - you don't want to cancel the a single command that was sent, >> which might mean the client has to know if multiple commands have been >> sent, and which ones. > > If the client waits for the acknowledgements before sending the repeat, isn't > there a risk that we handicap players with a high network latency? If the > acks are sent from the server immediately upon reception of the command, > perhaps this wouldn't be so bad in most cases. If the ack is sent after the > command has been executed, it would be a big problem. This is what the 'command window' property controls (comc/ncom protocol commands) Basically, the client won't send another command to the server if more the X commands are outstanding (have not been processed). The problem here is just as discussed for other issues - the client is not smart in what command should/should not be sent (that apply healing potion may be a bad one not to send) The default for the command window is 10 IIRC, which means that the client can have sent up to 10 outstanding commands to the server. In general, that number is too high, but can be easily set in the client. Note that this command acknowledge did not exist for a while in the protocol - the run_on/fire_on predate the existence of that feature. > > >> This reduces the server having to remember more state. >> >> In terms of what commands should repeat and what should not, this could >> probably be an option in the keybind menu, eg, 'repeat?' which notes if the >> command should be repeated or not. Probably also allow it in the >> keybindings itself, because for some commands, there may be desire to >> repeat them in certain circumstances and not other (apply immediately comes >> to mind in certain cases, like eating a lot of low value food, but not in >> others, like using exits) > > This is a great idea! It would also solve the problem with deciding whether > to cancel a queue of commands or not - only on releasing the key of a > repeating command should a "reset queue" command be sent to the server. And more specifically, rather than clear the entire queue, it would be 'clear the queue of the command that was just repeating'. You could have a very real case of a character moving in some direction and the player hitting another key to do something without releasing the movement key (if fire_on is removed, it could be something like holding down the up arrow, so north is getting repeated, and then player sees a monster, hits shift so arrow is fired, and then releases the up arrow (player is waiting for monster to approach) - in that case, you don't want to lose that the player fired north. >> I'd certainly say it is worth it to post it or upload it to sourceforge on >> the tracker. > > Which is the preferred method? I'm used to sending patches inline in emails; > the sourceforge tracker is new to me. If you think the patch is ready for inclusion (vs say demonstration code), then I think the tracker is the right place. It should be pretty simple - it just lets you upload a file after you create the item to track. This is especially good if the patch is long, as sending it to the entire list may be a bit much, yet at the same time, more than one person may want to look at it. From arvidb at kth.se Mon Oct 28 18:09:39 2013 From: arvidb at kth.se (Arvid Brodin) Date: Tue, 29 Oct 2013 00:09:39 +0100 Subject: [crossfire] [RFC 0/3] Work on keys.c and the keybinding system Message-ID: <526EEEB3.6030106@kth.se> Hi, This patch series is meant to make keybinding usage a lot more obvious - to new users especially, but hopefully also for the more experienced ones. Hopefully it also makes the keys.c file a bit more maintainable. In detail: * Coding style fixes (so that it is at least consistent in the keys.c file). * Show all keybindings (incl default ones) in both 'unbind' and the keybindings dialog. (Should reduce confusion for new users.) * Add "Any" key modifier type; New "Any" button in keybindings dialog. This lets the user choose between having a key work regardless of modifier (Ctrl, Alt, etc) key states, or to use the same key with different modifiers for different bindings. How this worked before ("All"/"Normal") was pretty unclear, even in the code comments. (New feature/bug fix.) * Consistent use of hash helper functions (keybind_insert(), keybind_remove(), etc). This should make the code more maintainable and reduce the risk of bugs. * Removed possibility to rebind Ctrl, Shift, Alt, Meta modifiers. This feature is very unusual and breaks the normal design pattern of using the designated keys as keyboard modifiers. * Better usage texts (among other things they used '[' which was interpreted as a style tag, making parts of the usage text go missing). (Bug fix.) * Bind only lowercase keys - we handle Shift state separately. (Bug fix.) * Rehash keybind entry on key dialog update (fixes bug where keybinding with modified keyval would become unusable until restart). (Bug fix.) * Removed -afmnr flags from 'bind' - instead use only "press the key combo you want" when you bind the key. This is to simplify the keybind without removing any real functionality. * Per-character keys files. I'm thinking that perhaps I should also change the flags in the keys file so that they conform with the modifier keys? Originally they were: [A]ll, [N]ormal, [F]ire, [R]un, A[L]t, [M]eta, [E]dit. Now I'm writing: [A]ny, [N]one, [F] Shift, [R] Ctrl, A[L]t, [M]eta, [E]dit. Perhaps [A]ny, [N]one, [S]hift, [C]trl, A[L]t, [M]eta, [E]dit. would be a better idea? It doesn't clash with the old characters, so it's easy to keep the capability of reading both old and new formats. This also fits nicely with patch #3 (character-specific keys file) in that we won't overwrite the old keys file, but just write new per-character ones in the new format. Maybe also change the -d flag of 'bind' to '-i' (ignore)? Or '-a' (but -a meant Alt before). Looking forward, I would like to remove the fire_on and run_on "hacks" and instead use a generic way to handle repeating keys. Then one would bind e.g. KP_6: walk east Ctrl + KP_6: attack east Shift + KP_6: fire east This all needs testing, so if you like it, please test it! It should apply to current Subversion trunk (rev 19081). -- Arvid From arvidb at kth.se Mon Oct 28 18:15:30 2013 From: arvidb at kth.se (Arvid Brodin) Date: Tue, 29 Oct 2013 00:15:30 +0100 Subject: [crossfire] [RFC 1/3] keys.c: Style fixes Message-ID: <526EF012.2070809@kth.se> Use consistent coding style throughout the file. Signed-off-by: Arvid Brodin --- gtk-v2/src/keys.c | 604 +++++++++++++++++++++++++++--------------------------- 1 file changed, 303 insertions(+), 301 deletions(-) diff --git a/gtk-v2/src/keys.c b/gtk-v2/src/keys.c index 1f39f77..d44a460 100644 --- a/gtk-v2/src/keys.c +++ b/gtk-v2/src/keys.c @@ -89,7 +89,7 @@ enum { #define MAX_COMMAND_LEN 256 char history[MAX_HISTORY][MAX_COMMAND_LEN]; -static int cur_history_position=0, scroll_history_position=0; +static int cur_history_position = 0, scroll_history_position = 0; /** * @} EndOf Bind Log */ @@ -125,7 +125,7 @@ static uint32 firekeysym[2], runkeysym[2], commandkeysym, *bind_keysym, prevkeysym, nextkeysym, completekeysym, altkeysym[2], metakeysym[2], cancelkeysym; -static int bind_flags=0; +static int bind_flags = 0; static char bind_buf[MAX_BUF]; #define KEYF_NORMAL 0x01 /**< Used in normal mode */ @@ -165,18 +165,18 @@ static Key_Entry *keys[KEYHASH]; /**< Platform independence defines that static void insert_key(uint32 keysym, int flags, const char *command) { Key_Entry *newkey; - int i, direction=-1, slot; + int i, direction = -1, slot; slot = keysym % KEYHASH; - if (keys[slot]==NULL) { - keys[slot]=malloc(sizeof(Key_Entry)); - keys[slot]->command=NULL; - keys[slot]->next=NULL; - newkey=keys[slot]; + if (keys[slot] == NULL) { + keys[slot] = malloc(sizeof(Key_Entry)); + keys[slot]->command = NULL; + keys[slot]->next = NULL; + newkey = keys[slot]; } else { - newkey=keys[slot]; - while (newkey->next!=NULL) { + newkey = keys[slot]; + while (newkey->next != NULL) { newkey = newkey->next; } newkey->next = calloc(1, sizeof(Key_Entry)); @@ -186,9 +186,9 @@ static void insert_key(uint32 keysym, int flags, const char *command) * Try to find out if the command is a direction command. If so, keep * track of this fact, so in fire or run mode, things work correctly. */ - for (i=0; i<9; i++) + for (i = 0; i < 9; i++) if (!strcmp(command, directions[i])) { - direction=i; + direction = i; break; } @@ -217,11 +217,13 @@ static void parse_keybind_line(char *buf, int line, int standard) */ cp = NULL; - if (buf[0]=='#' || buf[0]=='\n') { + if (buf[0] == '#' || buf[0] == '\n') { return; } - if ((cpnext = strchr(buf,' '))==NULL) { - LOG(LOG_WARNING,"gtk-v2::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line,buf); + cpnext = strchr(buf,' '); + if (cpnext == NULL) { + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Line %d (%s) corrupted in keybinding file.", line, buf); return; } /* Special keybinding line */ @@ -232,98 +234,106 @@ static void parse_keybind_line(char *buf, int line, int standard) } cp = strchr(cpnext, ' '); if (!cp) { - LOG(LOG_WARNING,"gtk-v2::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line,buf); + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Line %d (%s) corrupted in keybinding file.", line, buf); return; } *cp++ = 0; /* Null terminate it */ cp1 = strchr(cp, ' '); if (!cp1) { - LOG(LOG_WARNING,"gtk-v2::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line,buf); + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Line %d (%s) corrupted in keybinding file.", line, buf); return; } *cp1 ++ = 0;/* Null terminate it */ keysym = gdk_keyval_from_name(cp); /* As of now, all these keys must have keysyms */ if (keysym == 0) { - LOG(LOG_WARNING,"gtk-v2::parse_keybind_line","Could not convert %s into keysym", cp); + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Could not convert %s into keysym", cp); return; } - if (!strcmp(cpnext,"commandkey")) { + if (!strcmp(cpnext, "commandkey")) { commandkeysym = keysym; return; } - if (!strcmp(cpnext,"altkey0")) { + if (!strcmp(cpnext, "altkey0")) { altkeysym[0] = keysym; return; } - if (!strcmp(cpnext,"altkey1")) { + if (!strcmp(cpnext, "altkey1")) { altkeysym[1] = keysym; return; } - if (!strcmp(cpnext,"firekey0")) { + if (!strcmp(cpnext, "firekey0")) { firekeysym[0] = keysym; return; } - if (!strcmp(cpnext,"firekey1")) { + if (!strcmp(cpnext, "firekey1")) { firekeysym[1] = keysym; return; } - if (!strcmp(cpnext,"metakey0")) { + if (!strcmp(cpnext, "metakey0")) { metakeysym[0] = keysym; return; } - if (!strcmp(cpnext,"metakey1")) { + if (!strcmp(cpnext, "metakey1")) { metakeysym[1] = keysym; return; } - if (!strcmp(cpnext,"runkey0")) { + if (!strcmp(cpnext, "runkey0")) { runkeysym[0] = keysym; return; } - if (!strcmp(cpnext,"runkey1")) { + if (!strcmp(cpnext, "runkey1")) { runkeysym[1] = keysym; return; } - if (!strcmp(cpnext,"completekey")) { + if (!strcmp(cpnext, "completekey")) { completekeysym = keysym; return; } - if (!strcmp(cpnext,"nextkey")) { + if (!strcmp(cpnext, "nextkey")) { nextkeysym = keysym; return; } - if (!strcmp(cpnext,"prevkey")) { + if (!strcmp(cpnext, "prevkey")) { prevkeysym = keysym; return; } } else { if (standard) { - standard=KEYF_STANDARD; + standard = KEYF_STANDARD; } else { - standard=0; + standard = 0; } *cpnext++ = '\0'; keysym = gdk_keyval_from_name(buf); if (!keysym) { - LOG(LOG_WARNING,"gtk-v2::parse_keybind_line","Unable to convert line %d (%s) into keysym", line, cp); + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Unable to convert line %d (%s) into keysym", line, cp); return; } cp = cpnext; - if ((cpnext = strchr(cp,' '))==NULL) { - LOG(LOG_WARNING,"gtk-v2::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line, cp); + cpnext = strchr(cp,' '); + if (cpnext == NULL) { + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Line %d (%s) corrupted in keybinding file.", line, cp); return; } *cpnext++ = '\0'; cp = cpnext; - if ((cpnext = strchr(cp,' '))==NULL) { - LOG(LOG_WARNING,"gtk-v2::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line, cp); + cpnext = strchr(cp,' '); + if (cpnext == NULL) { + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Line %d (%s) corrupted in keybinding file.", line, cp); return; } *cpnext++ = '\0'; flags = 0; - while (*cp!='\0') { + while (*cp != '\0') { switch (*cp) { case 'A': flags |= KEYF_NORMAL | KEYF_FIRE | KEYF_RUN @@ -351,17 +361,18 @@ static void parse_keybind_line(char *buf, int line, int standard) flags |= KEYF_STANDARD; break; default: - LOG(LOG_WARNING,"gtk-v2::parse_keybind_line","Unknown flag (%c) line %d in key binding file", + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Unknown flag (%c) line %d in key binding file", *cp, line); } cp++; } /* Rest of the line is the actual command. Lets kill the newline */ - cpnext[strlen(cpnext)-1]='\0'; - if (strlen(cpnext)>(sizeof(bind_buf)-1)) { - cpnext[sizeof(bind_buf)-1]='\0'; - LOG(LOG_WARNING,"gtk-v2::parse_keybind_line","Command too long! Truncated."); + cpnext[strlen(cpnext) - 1] = '\0'; + if (strlen(cpnext) > (sizeof(bind_buf) - 1)) { + cpnext[sizeof(bind_buf) - 1] = '\0'; + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", "Command too long! Truncated."); } insert_key(keysym, flags | standard, cpnext); @@ -376,8 +387,7 @@ static void init_default_keybindings(void) char buf[MAX_BUF]; int i; - LOG(LOG_DEBUG, "gtk-v2::init_default_keybindings", - "Using built-in defaults"); + LOG(LOG_DEBUG, "gtk-v2::init_default_keybindings", "Using built-in defaults"); for (i = 0; i < sizeof(def_keys) / sizeof(char *); i++) { strcpy(buf, def_keys[i]); @@ -430,7 +440,7 @@ void keys_init(GtkWidget *window_root) nextkeysym = NoSymbol; prevkeysym = NoSymbol; - for (i=0; ikeysym!=0 && keyentry->keysym!=keysym) || + while (keyentry != NULL) { + if ((keyentry->keysym != 0 && keyentry->keysym != keysym) || (!(keyentry->flags & present_flags))) { - keyentry=keyentry->next; + keyentry = keyentry->next; continue; } first_match = keyentry; /* Try to find a prefect match */ - if ((keyentry->flags & KEYF_MODIFIERS)!= present_flags) { - keyentry=keyentry->next; + if ((keyentry->flags & KEYF_MODIFIERS) != present_flags) { + keyentry = keyentry->next; continue; } else { break; } } - if (first_match!=NULL) { + if (first_match != NULL) { if (first_match->flags & KEYF_EDIT) { strcpy(cpl.input_text, first_match->command); cpl.input_state = Command_Mode; - gtk_entry_set_text(GTK_ENTRY(entry_commands),cpl.input_text); - gtk_widget_grab_focus (GTK_WIDGET(entry_commands)); + gtk_entry_set_text(GTK_ENTRY(entry_commands), cpl.input_text); + gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); gtk_editable_select_region(GTK_EDITABLE(entry_commands), 0, 0); gtk_editable_set_position(GTK_EDITABLE(entry_commands), -1); return; } /* Some spells (dimension door) need a valid count value */ - cpl.count = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spinbutton_count)); + cpl.count = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinbutton_count)); - if (first_match->direction>=0) { + if (first_match->direction >= 0) { if (cpl.fire_on) { snprintf(buf, sizeof(buf), "fire %s", first_match->command); fire_dir(first_match->direction); @@ -750,35 +762,36 @@ static void parse_key(char key, uint32 keysym) } return; } - if (key>='0' && key<='9') { - cpl.count = cpl.count*10 + (key-'0'); - if (cpl.count>100000) { - cpl.count%=100000; + if (key >= '0' && key <= '9') { + cpl.count = cpl.count*10 + (key - '0'); + if (cpl.count > 100000) { + cpl.count %= 100000; } - gtk_spin_button_set_value (GTK_SPIN_BUTTON(spinbutton_count), (float) cpl.count ); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton_count), (float) cpl.count); return; } - tmpbuf[0]=0; + tmpbuf[0] = 0; if (cpl.fire_on) { - strcat(tmpbuf,"fire+"); + strcat(tmpbuf, "fire+"); } if (cpl.run_on) { - strcat(tmpbuf,"run+"); + strcat(tmpbuf, "run+"); } if (cpl.alt_on) { - strcat(tmpbuf,"alt+"); + strcat(tmpbuf, "alt+"); } if (cpl.meta_on) { - strcat(tmpbuf,"meta+"); + strcat(tmpbuf, "meta+"); } - snprintf(buf, sizeof(buf), "Key %s%s is not bound to any command. Use bind to associate this keypress with a command", - tmpbuf, keysym==NoSymbol? "unknown": gdk_keyval_name(keysym)); + snprintf(buf, sizeof(buf), + "Key %s%s is not bound to any command. Use bind to associate this keypress with a command", + tmpbuf, keysym == NoSymbol ? "unknown" : gdk_keyval_name(keysym)); #ifdef WIN32 - if ( ( 65513 != keysym ) && ( 65511 != keysym ) ) + if ((65513 != keysym) && (65511 != keysym)) #endif draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); - cpl.count=0; + cpl.count = 0; } /** @@ -789,54 +802,54 @@ static void parse_key(char key, uint32 keysym) * information in a friendly manner. * @return A character string describing the key. */ -static char * get_key_info(Key_Entry *key, int save_mode) +static char *get_key_info(Key_Entry *key, int save_mode) { /* bind buf is the maximum space allowed for a * binded command. We will add additional datas to * it so we increase by MAX_BUF*/ - static char buf[MAX_BUF+sizeof(bind_buf)]; + static char buf[MAX_BUF + sizeof(bind_buf)]; char buff[MAX_BUF]; - int bi=0; + int bi = 0; if ((key->flags & KEYF_MODIFIERS) == KEYF_MODIFIERS) { - buff[bi++] ='A'; + buff[bi++] = 'A'; } else { if (key->flags & KEYF_NORMAL) { - buff[bi++] ='N'; + buff[bi++] = 'N'; } if (key->flags & KEYF_FIRE) { - buff[bi++] ='F'; + buff[bi++] = 'F'; } if (key->flags & KEYF_RUN) { - buff[bi++] ='R'; + buff[bi++] = 'R'; } if (key->flags & KEYF_ALT) { - buff[bi++] ='L'; + buff[bi++] = 'L'; } if (key->flags & KEYF_META) { - buff[bi++] ='M'; + buff[bi++] = 'M'; } } if (key->flags & KEYF_EDIT) { - buff[bi++] ='E'; + buff[bi++] = 'E'; } if (key->flags & KEYF_STANDARD) { - buff[bi++] ='S'; + buff[bi++] = 'S'; } - buff[bi]='\0'; + buff[bi] = '\0'; if (save_mode) { - if(key->keysym == NoSymbol) { + if (key->keysym == NoSymbol) { snprintf(buf, sizeof(buf), "(null) %i %s %s", - 0,buff, key->command); + 0, buff, key->command); } else { snprintf(buf, sizeof(buf), "%s %i %s %s", gdk_keyval_name(key->keysym), 0, buff, key->command); } } else { - if(key->keysym == NoSymbol) { + if (key->keysym == NoSymbol) { snprintf(buf, sizeof(buf), "key (null) %s %s", buff, key->command); } else { @@ -855,56 +868,56 @@ static char * get_key_info(Key_Entry *key, int save_mode) */ static void show_keys(int allbindings) { - int i, count=1; + int i, count = 1; Key_Entry *key; char buf[MAX_BUF]; snprintf(buf, sizeof(buf), "Commandkey %s", - commandkeysym==NoSymbol?"unknown":gdk_keyval_name(commandkeysym)); + commandkeysym == NoSymbol ? "unknown" : gdk_keyval_name(commandkeysym)); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); snprintf(buf, sizeof(buf), "Firekeys 1: %s, 2: %s", - firekeysym[0]==NoSymbol?"unknown":gdk_keyval_name(firekeysym[0]), - firekeysym[1]==NoSymbol?"unknown":gdk_keyval_name(firekeysym[1])); + firekeysym[0] == NoSymbol ? "unknown" : gdk_keyval_name(firekeysym[0]), + firekeysym[1] == NoSymbol ? "unknown" : gdk_keyval_name(firekeysym[1])); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); snprintf(buf, sizeof(buf), "Altkeys 1: %s, 2: %s", - altkeysym[0]==NoSymbol?"unknown":gdk_keyval_name(altkeysym[0]), - altkeysym[1]==NoSymbol?"unknown":gdk_keyval_name(altkeysym[1])); + altkeysym[0] == NoSymbol ? "unknown" : gdk_keyval_name(altkeysym[0]), + altkeysym[1] == NoSymbol ? "unknown" : gdk_keyval_name(altkeysym[1])); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); snprintf(buf, sizeof(buf), "Metakeys 1: %s, 2: %s", - metakeysym[0]==NoSymbol?"unknown":gdk_keyval_name(metakeysym[0]), - metakeysym[1]==NoSymbol?"unknown":gdk_keyval_name(metakeysym[1])); + metakeysym[0] == NoSymbol ? "unknown" : gdk_keyval_name(metakeysym[0]), + metakeysym[1] == NoSymbol ? "unknown" : gdk_keyval_name(metakeysym[1])); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); snprintf(buf, sizeof(buf), "Runkeys 1: %s, 2: %s", - runkeysym[0]==NoSymbol?"unknown":gdk_keyval_name(runkeysym[0]), - runkeysym[1]==NoSymbol?"unknown":gdk_keyval_name(runkeysym[1])); + runkeysym[0] == NoSymbol ? "unknown" : gdk_keyval_name(runkeysym[0]), + runkeysym[1] == NoSymbol ? "unknown" : gdk_keyval_name(runkeysym[1])); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); snprintf(buf, sizeof(buf), "Command Completion Key %s", - completekeysym==NoSymbol?"unknown":gdk_keyval_name(completekeysym)); + completekeysym == NoSymbol ? "unknown" : gdk_keyval_name(completekeysym)); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); snprintf(buf, sizeof(buf), "Next Command in History Key %s", - nextkeysym==NoSymbol?"unknown":gdk_keyval_name(nextkeysym)); + nextkeysym == NoSymbol ? "unknown" : gdk_keyval_name(nextkeysym)); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); snprintf(buf, sizeof(buf), "Previous Command in History Key %s", - prevkeysym==NoSymbol?"unknown":gdk_keyval_name(prevkeysym)); + prevkeysym == NoSymbol ? "unknown" : gdk_keyval_name(prevkeysym)); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); /* * Perhaps we should start at 8, so that we only show 'active' keybindings? */ - for (i=0; inext) { + for (i = 0; i < KEYHASH; i++) { + for (key = keys[i]; key != NULL; key = key->next) { if (key->flags & KEYF_STANDARD && !allbindings) { continue; } - snprintf(buf, sizeof(buf), "%3d %s",count, get_key_info(key,0)); + snprintf(buf, sizeof(buf), "%3d %s", count, get_key_info(key, 0)); draw_ext_info( NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); count++; @@ -933,7 +946,7 @@ void bind_key(char *params) } /* Skip over any spaces we may have */ - while (*params==' ') { + while (*params == ' ') { params++; } @@ -946,42 +959,42 @@ void bind_key(char *params) } if (!strcmp(params, "firekey1")) { - bind_keysym = & firekeysym[0]; + bind_keysym = &firekeysym[0]; draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, "Push key to bind new firekey 1."); cpl.input_state = Configure_Keys; return; } if (!strcmp(params, "firekey2")) { - bind_keysym = & firekeysym[1]; + bind_keysym = &firekeysym[1]; draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, "Push key to bind new firekey 2."); cpl.input_state = Configure_Keys; return; } if (!strcmp(params, "metakey1")) { - bind_keysym = & metakeysym[0]; + bind_keysym = &metakeysym[0]; draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, "Push key to bind new metakey 1."); cpl.input_state = Configure_Keys; return; } if (!strcmp(params, "metakey2")) { - bind_keysym = & metakeysym[1]; + bind_keysym = &metakeysym[1]; draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, "Push key to bind new metakey 2."); cpl.input_state = Configure_Keys; return; } if (!strcmp(params, "altkey1")) { - bind_keysym = & altkeysym[0]; + bind_keysym = &altkeysym[0]; draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, "Push key to bind new altkey 1."); cpl.input_state = Configure_Keys; return; } if (!strcmp(params, "altkey2")) { - bind_keysym = & altkeysym[1]; + bind_keysym = &altkeysym[1]; draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, "Push key to bind new altkey 2."); cpl.input_state = Configure_Keys; @@ -1030,8 +1043,8 @@ void bind_key(char *params) if (params[0] != '-') { bind_flags = KEYF_MODIFIERS; } else { - bind_flags =0; - bind_keysym=NULL; + bind_flags = 0; + bind_keysym = NULL; for (params++; *params != ' '; params++) switch (*params) { case 'a': @@ -1066,7 +1079,7 @@ void bind_key(char *params) params++; } - if (! (bind_flags & KEYF_MODIFIERS)) { + if (!(bind_flags & KEYF_MODIFIERS)) { bind_flags |= KEYF_MODIFIERS; } @@ -1104,7 +1117,7 @@ void bind_key(char *params) */ static void save_individual_key(FILE *fp, Key_Entry *key, KeyCode kc) { - if (key==NULL) { + if (key == NULL) { return; } fprintf(fp, "%s\n", get_key_info(key, 1)); @@ -1146,11 +1159,12 @@ static void save_keys(void) CONVERT_FILESPEC_TO_OS_FORMAT(buf); #endif - if (make_path_to_file(buf)==-1) { - LOG(LOG_WARNING,"gtk-v2::save_keys","Could not create %s", buf); + if (make_path_to_file(buf) == -1) { + LOG(LOG_WARNING, "gtk-v2::save_keys", "Could not create %s", buf); return; } - if ((fp=fopen(buf,"w"))==NULL) { + fp = fopen(buf,"w"); + if (fp == NULL) { snprintf(buf2, sizeof(buf2), "Could not open %s, key bindings not saved\n", buf); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_ERROR, buf2); return; @@ -1221,29 +1235,29 @@ static void save_keys(void) static void configure_keys(uint32 keysym) { char buf[MAX_BUF]; - Key_Entry *keyentry, *first_match=NULL; + Key_Entry *keyentry, *first_match = NULL; /* * I think that basically if we are not rebinding the special control keys * (in which case bind_kesym would be set to something) we just want to * handle these keypresses as normal events. */ - if (bind_keysym==NULL) { - if(keysym == altkeysym[0] || keysym == altkeysym[1]) { - cpl.alt_on =1; + if (bind_keysym == NULL) { + if (keysym == altkeysym[0] || keysym == altkeysym[1]) { + cpl.alt_on = 1; return; } - if(keysym == metakeysym[0] || keysym == metakeysym[1]) { - cpl.meta_on =1; + if (keysym == metakeysym[0] || keysym == metakeysym[1]) { + cpl.meta_on = 1; return; } - if(keysym == firekeysym[0] || keysym == firekeysym[1]) { - cpl.fire_on =1; + if (keysym == firekeysym[0] || keysym == firekeysym[1]) { + cpl.fire_on = 1; draw_message_window(0); return; } - if(keysym == runkeysym[0] || keysym == runkeysym[1]) { - cpl.run_on =1; + if (keysym == runkeysym[0] || keysym == runkeysym[1]) { + cpl.run_on = 1; draw_message_window(0); return; } @@ -1255,7 +1269,7 @@ static void configure_keys(uint32 keysym) * flags to bind for many simple binds. */ if ((cpl.fire_on || cpl.run_on || cpl.meta_on || cpl.alt_on) && - (bind_flags & KEYF_MODIFIERS)==KEYF_MODIFIERS) { + (bind_flags & KEYF_MODIFIERS) == KEYF_MODIFIERS) { bind_flags &= ~KEYF_MODIFIERS; if (cpl.fire_on) { bind_flags |= KEYF_FIRE; @@ -1271,29 +1285,31 @@ static void configure_keys(uint32 keysym) } } - if (bind_keysym!=NULL) { - *bind_keysym=keysym; - bind_keysym=NULL; + if (bind_keysym != NULL) { + *bind_keysym = keysym; + bind_keysym = NULL; } else { keyentry = keys[keysym % KEYHASH]; - while (keyentry!=NULL) { - if ((keyentry->keysym!=0 && keyentry->keysym!=keysym) || + while (keyentry != NULL) { + if ((keyentry->keysym != 0 && keyentry->keysym != keysym) || (!(keyentry->flags & bind_flags))) { - keyentry=keyentry->next; + keyentry = keyentry->next; continue; } first_match = keyentry; /* Try to find a prefect match */ - if ((keyentry->flags & KEYF_MODIFIERS)!= bind_flags) { - keyentry=keyentry->next; + if ((keyentry->flags & KEYF_MODIFIERS) != bind_flags) { + keyentry = keyentry->next; continue; } else { break; } } if (first_match) { - snprintf(buf, sizeof(buf), "Warning: Keybind %s may conflict with new binding.", first_match->command); + snprintf(buf, sizeof(buf), + "Warning: Keybind %s may conflict with new binding.", + first_match->command); draw_ext_info( NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_ERROR, buf); } @@ -1302,10 +1318,10 @@ static void configure_keys(uint32 keysym) } snprintf(buf, sizeof(buf), "Binded to key '%s' (%i)", - keysym==NoSymbol?"unknown":gdk_keyval_name(keysym), keysym); + keysym == NoSymbol ? "unknown" : gdk_keyval_name(keysym), keysym); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_CONFIG, buf); - cpl.fire_on=0; - cpl.run_on=0; + cpl.fire_on = 0; + cpl.run_on = 0; draw_message_window(0); /* @@ -1338,43 +1354,45 @@ static void unbind_usage(void) */ void unbind_key(const char *params) { - int count=0, keyentry, onkey,global=0; + int count = 0, keyentry, onkey,global = 0; Key_Entry *key, *tmp; char buf[MAX_BUF]; - if (params==NULL || params[0]=='\0') { + if (params == NULL || params[0] == '\0') { show_keys(0); return; } /* Skip over any spaces we may have */ - while (*params==' ') { + while (*params == ' ') { params++; } - if (!strcmp(params,"-a")) { + if (!strcmp(params, "-a")) { show_keys(1); return; } - if (!strncmp(params,"-g",2)) { - global=1; - if (!(params=strchr(params,' '))) { + if (!strncmp(params, "-g", 2)) { + global = 1; + params = strchr(params,' '); + if (params == NULL) { unbind_usage(); return; } } - if ((keyentry=atoi(params))==0) { + keyentry = atoi(params); + if (keyentry == 0) { unbind_usage(); return; } - for (onkey=0; onkeynext) { - if (global || !(key->flags&KEYF_STANDARD)) { + for (onkey = 0; onkey < KEYHASH; onkey++) { + for (key = keys[onkey]; key; key = key->next) { + if (global || !(key->flags & KEYF_STANDARD)) { count++; } /* We found the key we want to unbind */ - if (keyentry==count) { + if (keyentry == count) { /* If it is the first entry, it is easy */ if (key == keys[onkey]) { @@ -1385,9 +1403,9 @@ void unbind_key(const char *params) * Otherwise, we need to figure out where in the link list the * entry is. */ - for (tmp=keys[onkey]; tmp->next!=NULL; tmp=tmp->next) { + for (tmp = keys[onkey]; tmp->next != NULL; tmp = tmp->next) { if (tmp->next == key) { - tmp->next =key->next; + tmp->next = key->next; goto unbinded; } } @@ -1418,24 +1436,24 @@ unbinded: */ void focusoutfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) { - if (cpl.fire_on==1) { - cpl.fire_on=0; + if (cpl.fire_on == 1) { + cpl.fire_on = 0; clear_fire(); - gtk_label_set (GTK_LABEL(fire_label)," "); + gtk_label_set(GTK_LABEL(fire_label), " "); } - if (cpl.run_on==1) { - cpl.run_on=0; + if (cpl.run_on == 1) { + cpl.run_on = 0; if (use_config[CONFIG_ECHO]) draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, "stop run"); clear_run(); - gtk_label_set (GTK_LABEL(run_label)," "); + gtk_label_set(GTK_LABEL(run_label), " "); } - if (cpl.alt_on==1) { - cpl.alt_on=0; + if (cpl.alt_on == 1) { + cpl.alt_on = 0; } - if (cpl.meta_on==1) { - cpl.meta_on=0; + if (cpl.meta_on == 1) { + cpl.meta_on = 0; } } @@ -1448,10 +1466,10 @@ void focusoutfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) */ void keyrelfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) { - if (event->keyval > 0 && ! GTK_WIDGET_HAS_FOCUS(entry_commands)) { + if (event->keyval > 0 && !GTK_WIDGET_HAS_FOCUS(entry_commands)) { parse_key_release(event->keyval); } - g_signal_stop_emission_by_name(GTK_OBJECT (window), "key_release_event"); + g_signal_stop_emission_by_name(GTK_OBJECT(window), "key_release_event"); } /** @@ -1478,11 +1496,11 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) if (csocket_fd) { gdk_input_remove(csocket_fd); - csocket_fd=0; + csocket_fd = 0; gtk_main_quit(); } g_signal_stop_emission_by_name( - GTK_OBJECT (window), "key_press_event"); + GTK_OBJECT(window), "key_press_event"); return; } if (cpl.input_state == Reply_One) { @@ -1490,16 +1508,16 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) send_reply(text); cpl.input_state = Playing; g_signal_stop_emission_by_name( - GTK_OBJECT (window), "key_press_event"); + GTK_OBJECT(window), "key_press_event"); return; } else if (cpl.input_state == Reply_Many) { if (GTK_WIDGET_HAS_FOCUS (entry_commands)) { gtk_widget_event(GTK_WIDGET(entry_commands), (GdkEvent*)event); } else { - gtk_widget_grab_focus (GTK_WIDGET(entry_commands)); + gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); } g_signal_stop_emission_by_name( - GTK_OBJECT (window), "key_press_event"); + GTK_OBJECT(window), "key_press_event"); return; } } @@ -1508,7 +1526,7 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) * handling these key values. */ if (event->keyval > 0) { - if (GTK_WIDGET_HAS_FOCUS (entry_commands)) { + if (GTK_WIDGET_HAS_FOCUS(entry_commands)) { if (event->keyval == completekeysym) { gtk_complete_command(); } @@ -1525,22 +1543,22 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) * will want to go the previous command when nothing is * entered in the command window. */ - if (event->keyval == prevkeysym - || event->keyval == nextkeysym) { - gtk_command_history(event->keyval == nextkeysym?0:1); + if ((event->keyval == prevkeysym) + || (event->keyval == nextkeysym)) { + gtk_command_history(event->keyval == nextkeysym ? 0 : 1); } else { if (cpl.run_on) { if (!(event->state & GDK_CONTROL_MASK)) { /* printf("Run is on while ctrl is not\n"); */ - gtk_label_set (GTK_LABEL(run_label)," "); - cpl.run_on=0; + gtk_label_set(GTK_LABEL(run_label), " "); + cpl.run_on = 0; stop_run(); } } if (cpl.fire_on) { if (!(event->state & GDK_SHIFT_MASK)) { /* printf("Fire is on while shift is not\n");*/ - gtk_label_set (GTK_LABEL(fire_label)," "); + gtk_label_set(GTK_LABEL(fire_label), " "); cpl.fire_on = 0; stop_fire(); } @@ -1548,7 +1566,7 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) if ( (event->state & GDK_CONTROL_MASK) && (event->state & GDK_SHIFT_MASK) - && (event->keyval==GDK_i || event->keyval==GDK_I) ) { + && (event->keyval == GDK_i || event->keyval == GDK_I) ) { reset_map(); } @@ -1561,14 +1579,14 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) break; case Command_Mode: - if ( event->keyval == completekeysym ) { + if (event->keyval == completekeysym) { gtk_complete_command(); } - if ( event->keyval == prevkeysym - || event->keyval == nextkeysym ) { - gtk_command_history(event->keyval == nextkeysym?0:1); + if ((event->keyval == prevkeysym) + || (event->keyval == nextkeysym)) { + gtk_command_history(event->keyval == nextkeysym ? 0 : 1); } else { - gtk_widget_grab_focus (GTK_WIDGET(entry_commands)); + gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); /* * When running in split windows mode, entry_commands * can't get focus because it is in a different @@ -1587,7 +1605,7 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) break; case Metaserver_Select: - gtk_widget_grab_focus (GTK_WIDGET(entry_commands)); + gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); break; default: @@ -1597,7 +1615,7 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) } } g_signal_stop_emission_by_name( - GTK_OBJECT (window), "key_press_event"); + GTK_OBJECT(window), "key_press_event"); } /** @@ -1605,66 +1623,66 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) * * @param keylist */ -void draw_keybindings (GtkWidget *keylist) +void draw_keybindings(GtkWidget *keylist) { - int i, count=1; + int i, count = 1; Key_Entry *key; - int allbindings=0; + int allbindings = 0; char buff[MAX_BUF]; - int bi=0; + int bi = 0; char buffer[5][MAX_BUF]; char *buffers[5]; gint tmprow; gtk_clist_clear (GTK_CLIST(keylist)); - for (i=0; inext) { + for (i = 0; i < KEYHASH; i++) { + for (key = keys[i]; key != NULL; key = key->next) { if (key->flags & KEYF_STANDARD && !allbindings) { continue; } - bi=0; + bi = 0; if ((key->flags & KEYF_MODIFIERS) == KEYF_MODIFIERS) { - buff[bi++] ='A'; + buff[bi++] = 'A'; } else { if (key->flags & KEYF_NORMAL) { - buff[bi++] ='N'; + buff[bi++] = 'N'; } if (key->flags & KEYF_FIRE) { - buff[bi++] ='F'; + buff[bi++] = 'F'; } if (key->flags & KEYF_RUN) { - buff[bi++] ='R'; + buff[bi++] = 'R'; } if (key->flags & KEYF_ALT) { - buff[bi++] ='L'; + buff[bi++] = 'L'; } if (key->flags & KEYF_META) { - buff[bi++] ='M'; + buff[bi++] = 'M'; } } if (key->flags & KEYF_EDIT) { - buff[bi++] ='E'; + buff[bi++] = 'E'; } if (key->flags & KEYF_STANDARD) { - buff[bi++] ='S'; + buff[bi++] = 'S'; } - buff[bi]='\0'; + buff[bi] = '\0'; - if(key->keysym != NoSymbol) { - snprintf(buffer[0], sizeof(buffer[0]), "%i",count); + if (key->keysym != NoSymbol) { + snprintf(buffer[0], sizeof(buffer[0]), "%i", count); snprintf(buffer[1], sizeof(buffer[1]), "%s", gdk_keyval_name(key->keysym)); - snprintf(buffer[2], sizeof(buffer[2]), "%i",i); - snprintf(buffer[3], sizeof(buffer[3]), "%s",buff); + snprintf(buffer[2], sizeof(buffer[2]), "%i", i); + snprintf(buffer[3], sizeof(buffer[3]), "%s", buff); snprintf(buffer[4], sizeof(buffer[4]), "%s", key->command); buffers[0] = buffer[0]; buffers[1] = buffer[1]; buffers[2] = buffer[2]; buffers[3] = buffer[3]; buffers[4] = buffer[4]; - tmprow = gtk_clist_append (GTK_CLIST (keylist), buffers); + tmprow = gtk_clist_append(GTK_CLIST(keylist), buffers); } count++; } @@ -1687,7 +1705,7 @@ void x_set_echo(void) void draw_prompt(const char *str) { draw_ext_info(NDI_WHITE, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, str); - gtk_widget_grab_focus (GTK_WIDGET(entry_commands)); + gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); } /** @@ -1697,18 +1715,18 @@ void draw_prompt(const char *str) */ void gtk_command_history(int direction) { - int i=scroll_history_position; + int i = scroll_history_position; if (direction) { i--; - if (i<0) { - i+=MAX_HISTORY; + if (i < 0) { + i += MAX_HISTORY; } if (i == cur_history_position) { return; } } else { i++; - if (i>=MAX_HISTORY) { + if (i >= MAX_HISTORY) { i = 0; } if (i == cur_history_position) { @@ -1730,7 +1748,7 @@ void gtk_command_history(int direction) scroll_history_position=i; /* fprintf(stderr,"resetting postion to %d, data = %s\n", i, history[i]);*/ gtk_entry_set_text(GTK_ENTRY(entry_commands), history[i]); - gtk_widget_grab_focus (GTK_WIDGET(entry_commands)); + gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); gtk_editable_select_region(GTK_EDITABLE(entry_commands), 0, 0); gtk_editable_set_position(GTK_EDITABLE(entry_commands), -1); @@ -1752,7 +1770,7 @@ void gtk_complete_command(void) /* value differ, so update window */ if (newcommand != NULL) { gtk_entry_set_text(GTK_ENTRY(entry_commands), newcommand); - gtk_widget_grab_focus (GTK_WIDGET(entry_commands)); + gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); gtk_editable_select_region(GTK_EDITABLE(entry_commands), 0, 0); gtk_editable_set_position(GTK_EDITABLE(entry_commands), -1); } @@ -1765,9 +1783,7 @@ void gtk_complete_command(void) * @param entry * @param user_data */ -void -on_entry_commands_activate (GtkEntry *entry, - gpointer user_data) +void on_entry_commands_activate(GtkEntry *entry, gpointer user_data) { const gchar *entry_text; extern GtkWidget *treeview_look; @@ -1779,7 +1795,7 @@ on_entry_commands_activate (GtkEntry *entry, entry_text = gtk_entry_get_text(GTK_ENTRY(entry)); - if (cpl.input_state==Metaserver_Select) { + if (cpl.input_state == Metaserver_Select) { strcpy(cpl.input_text, entry_text); } else if (cpl.input_state == Reply_One || cpl.input_state == Reply_Many) { cpl.input_state = Playing; @@ -1793,19 +1809,19 @@ on_entry_commands_activate (GtkEntry *entry, } else { cpl.input_state = Playing; /* Some spells (dimension door) need a valid count value */ - cpl.count = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spinbutton_count)); + cpl.count = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinbutton_count)); /* No reason to do anything for a null string */ if (entry_text[0] != 0) { strncpy(history[cur_history_position], entry_text, MAX_COMMAND_LEN); - history[cur_history_position][MAX_COMMAND_LEN-1] = 0; + history[cur_history_position][MAX_COMMAND_LEN - 1] = 0; cur_history_position++; cur_history_position %= MAX_HISTORY; scroll_history_position = cur_history_position; extended_command(entry_text); } } - gtk_entry_set_text(GTK_ENTRY(entry),""); + gtk_entry_set_text(GTK_ENTRY(entry), ""); /* * This grab focus is really just so the entry box doesn't have focus - @@ -1813,10 +1829,10 @@ on_entry_commands_activate (GtkEntry *entry, * goes into the entry box. It doesn't make much difference what widget * this is set to, as long as it is something that can get focus. */ - gtk_widget_grab_focus (GTK_WIDGET(treeview_look)); + gtk_widget_grab_focus(GTK_WIDGET(treeview_look)); if( cpl.input_state == Metaserver_Select) { - cpl.input_state= Playing; + cpl.input_state = Playing; /* * This is the gtk_main that is started up by get_metaserver The client * will start another one once it is connected to a crossfire server @@ -1839,15 +1855,15 @@ on_entry_commands_activate (GtkEntry *entry, */ void update_keybinding_list(void) { - int i, allbindings=0; + int i, allbindings = 0; Key_Entry *key; - char modifier_buf[256]; + char modifier_buf[256]; GtkTreeIter iter; gtk_list_store_clear(keybinding_store); - for (i=0; inext) { + for (i = 0; i < KEYHASH; i++) { + for (key = keys[i]; key != NULL; key = key->next) { if (key->flags & KEYF_STANDARD && !allbindings) { continue; } @@ -1856,29 +1872,29 @@ void update_keybinding_list(void) if ((key->flags & KEYF_MODIFIERS) != KEYF_MODIFIERS) { if (key->flags & KEYF_ALT) { - strcat(modifier_buf,"Alt "); + strcat(modifier_buf, "Alt "); } if (key->flags & KEYF_FIRE) { - strcat(modifier_buf,"Fire "); + strcat(modifier_buf, "Fire "); } if (key->flags & KEYF_RUN) { - strcat(modifier_buf,"Run "); + strcat(modifier_buf, "Run "); } if (key->flags & KEYF_META) { - strcat(modifier_buf,"Meta "); + strcat(modifier_buf, "Meta "); } } else { strcat(modifier_buf, "All "); } if (key->flags & KEYF_STANDARD) { - strcat(modifier_buf,"(Standard) "); + strcat(modifier_buf, "(Standard) "); } gtk_list_store_append(keybinding_store, &iter); gtk_list_store_set(keybinding_store, &iter, KLIST_ENTRY, i, - KLIST_KEY, gdk_keyval_name(key->keysym), + KLIST_KEY, gdk_keyval_name(key->keysym), KLIST_MODS, modifier_buf, - KLIST_EDIT, (key->flags & KEYF_EDIT) ? "Yes":"No", + KLIST_EDIT, (key->flags & KEYF_EDIT) ? "Yes" : "No", KLIST_COMMAND, key->command, KLIST_KEY_ENTRY, key, -1); @@ -1893,9 +1909,7 @@ void update_keybinding_list(void) * @param menuitem * @param user_data */ -void -on_keybindings_activate (GtkMenuItem *menuitem, - gpointer user_data) +void on_keybindings_activate(GtkMenuItem *menuitem, gpointer user_data) { gtk_widget_show(keybinding_window); update_keybinding_list(); @@ -1913,10 +1927,8 @@ on_keybindings_activate (GtkMenuItem *menuitem, * @param user_data * @return TRUE (Returning TRUE prevents widget from getting this event.) */ -gboolean -on_keybinding_entry_key_key_press_event(GtkWidget *widget, - GdkEventKey *event, - gpointer user_data) +gboolean on_keybinding_entry_key_key_press_event(GtkWidget *widget, + GdkEventKey *event, gpointer user_data) { gtk_entry_set_text( GTK_ENTRY(keybinding_entry_key), gdk_keyval_name(event->keyval)); @@ -1968,13 +1980,11 @@ on_keybinding_entry_key_key_press_event(GtkWidget *widget, * @param button * @param user_data */ -void -on_keybinding_button_remove_clicked (GtkButton *button, - gpointer user_data) +void on_keybinding_button_remove_clicked(GtkButton *button, gpointer user_data) { - GtkTreeModel *model; + GtkTreeModel *model; GtkTreeIter iter; - Key_Entry *entry, *key, *tmp; + Key_Entry *entry, *key, *tmp; int onkey; if (!gtk_tree_selection_get_selected(keybinding_selection, &model, &iter)) { @@ -2056,7 +2066,7 @@ static void keybinding_get_data(uint32 *keysym, uint8 *flags, const char **comma * Meta checkboxes are checked. This closely matches the combined existing * logic in the parse_key() and get_key_info() functions. */ - if (! *flags || (*flags | KEYF_NORMAL) == KEYF_MODIFIERS) { + if (!*flags || (*flags | KEYF_NORMAL) == KEYF_MODIFIERS) { *flags |= KEYF_NORMAL; } @@ -2069,8 +2079,8 @@ static void keybinding_get_data(uint32 *keysym, uint8 *flags, const char **comma if (strlen(ed) >= sizeof(bind_buf)) { draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_ERROR, "Keybinding too long! Truncated."); - strncpy(bind_buf, ed, MAX_BUF-1); - bind_buf[MAX_BUF-1] = 0; + strncpy(bind_buf, ed, MAX_BUF - 1); + bind_buf[MAX_BUF - 1] = 0; *command = bind_buf; } else { *command = ed; @@ -2095,12 +2105,10 @@ static void keybinding_get_data(uint32 *keysym, uint8 *flags, const char **comma * @param button * @param user_data */ -void -on_keybinding_button_bind_clicked (GtkButton *button, - gpointer user_data) +void on_keybinding_button_bind_clicked(GtkButton *button, gpointer user_data) { - uint32 keysym; - uint8 flags; + uint32 keysym; + uint8 flags; const char *command; keybinding_get_data(&keysym, &flags, &command); @@ -2127,13 +2135,11 @@ on_keybinding_button_bind_clicked (GtkButton *button, * @param button * @param user_data */ -void -on_keybinding_button_update_clicked (GtkButton *button, - gpointer user_data) +void on_keybinding_button_update_clicked(GtkButton *button, gpointer user_data) { GtkTreeIter iter; - Key_Entry *entry; - GtkTreeModel *model; + Key_Entry *entry; + GtkTreeModel *model; const char *buf; if (gtk_tree_selection_get_selected(keybinding_selection, &model, &iter)) { @@ -2161,9 +2167,7 @@ on_keybinding_button_update_clicked (GtkButton *button, * @param button * @param user_data */ -void -on_keybinding_button_close_clicked (GtkButton *button, - gpointer user_data) +void on_keybinding_button_close_clicked(GtkButton *button, gpointer user_data) { gtk_widget_hide(keybinding_window); } @@ -2180,7 +2184,7 @@ on_keybinding_button_close_clicked (GtkButton *button, * @param userdata * @return TRUE */ -gboolean keybinding_selection_func ( +gboolean keybinding_selection_func( GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, @@ -2276,11 +2280,9 @@ void reset_keybinding_status(void) * @param button * @param user_data */ -void -on_keybinding_button_clear_clicked (GtkButton *button, - gpointer user_data) +void on_keybinding_button_clear_clicked(GtkButton *button, gpointer user_data) { - GtkTreeModel *model; + GtkTreeModel *model; GtkTreeIter iter; /* @@ -2288,7 +2290,7 @@ on_keybinding_button_clear_clicked (GtkButton *button, * deselect the currently selected keybinding if there is one. */ if (gtk_tree_selection_get_selected(keybinding_selection, &model, &iter)) { - gtk_tree_selection_unselect_iter (keybinding_selection, &iter); + gtk_tree_selection_unselect_iter(keybinding_selection, &iter); } reset_keybinding_status(); /* Clear inputs and reset buttons. */ } -- 1.8.1.5 -- Arvid From arvidb at kth.se Mon Oct 28 18:19:14 2013 From: arvidb at kth.se (Arvid Brodin) Date: Tue, 29 Oct 2013 00:19:14 +0100 Subject: [crossfire] [RFC 2/3] Misc keybinding fixes and changes Message-ID: <526EF0F2.7000208@kth.se> * Show all keybindings (incl default ones) in both 'unbind' and the keybindings dialog. (Should reduce confusion for new users.) * Add "Any" key modifier type; New "Any" button in keybindings dialog. This lets the user choose between having a key work regardless of modifier (Ctrl, Alt, etc) key states, or to use the same key with different modifiers for different bindings. How this worked before ("All"/"Normal") was pretty unclear, even in the code comments. (New feature/bug fix.) * Consistent use of hash helper functions (keybind_insert(), keybind_remove(), etc). This should make the code more maintainable and reduce the risk of bugs. * Removed possibility to rebind Ctrl, Shift, Alt, Meta modifiers. This feature is very unusual and breaks the normal design pattern of using the designated keys as keyboard modifiers. * Better usage texts (among other things they used '[' which was interpreted as a style tag, making parts of the usage text go missing). (Bug fix.) * Bind only lowercase keys - we handle Shift state separately. (Bug fix.) * Rehash keybind entry on key dialog update (fixes bug where keybinding with modified keyval would become unusable until restart). (Bug fix.) * Removed -afmnr flags from 'bind' - instead use only "press the key combo you want" when you bind the key. This is to simplify the keybind without removing any real functionality. Signed-off-by: Arvid Brodin --- gtk-v2/glade/dialogs.glade | 28 +- gtk-v2/src/gtk2proto.h | 1 + gtk-v2/src/keys.c | 902 +++++++++++++++++++++------------------------ 3 files changed, 439 insertions(+), 492 deletions(-) diff --git a/gtk-v2/glade/dialogs.glade b/gtk-v2/glade/dialogs.glade index a8b34e6..f9e65f7 100644 --- a/gtk-v2/glade/dialogs.glade +++ b/gtk-v2/glade/dialogs.glade @@ -3671,8 +3671,22 @@ this is the pricing the character gets in shops. False True + + Any + True + True + False + True + + + True + True + 0 + + + - Run + Ctrl True True False @@ -3682,12 +3696,12 @@ this is the pricing the character gets in shops. False False - 0 + 1 - Fire + Shift True True False @@ -3697,7 +3711,7 @@ this is the pricing the character gets in shops. False False - 1 + 2 @@ -3712,7 +3726,7 @@ this is the pricing the character gets in shops. False False - 2 + 3 @@ -3727,7 +3741,7 @@ this is the pricing the character gets in shops. False False - 3 + 4 @@ -3745,7 +3759,7 @@ this is the pricing the character gets in shops. False False - 4 + 5 diff --git a/gtk-v2/src/gtk2proto.h b/gtk-v2/src/gtk2proto.h index 2dc3d7c..26f7c6e 100644 --- a/gtk-v2/src/gtk2proto.h +++ b/gtk-v2/src/gtk2proto.h @@ -131,6 +131,7 @@ extern void on_keybinding_button_remove_clicked(GtkButton *button, gpointer user extern void on_keybinding_button_bind_clicked(GtkButton *button, gpointer user_data); extern void on_keybinding_button_update_clicked(GtkButton *button, gpointer user_data); extern void on_keybinding_button_close_clicked(GtkButton *button, gpointer user_data); +extern void on_keybinding_checkbutton_any_clicked(GtkCheckButton *cb, gpointer user_data); extern gboolean keybinding_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer userdata); extern void reset_keybinding_status(void); extern void on_keybinding_button_clear_clicked(GtkButton *button, gpointer user_data); diff --git a/gtk-v2/src/keys.c b/gtk-v2/src/keys.c index d44a460..fa91675 100644 --- a/gtk-v2/src/keys.c +++ b/gtk-v2/src/keys.c @@ -52,6 +52,7 @@ typedef int KeyCode; /**< Undefined type */ * Widgets for the keybinding dialog */ static GtkWidget *fire_label, *run_label, *keybinding_window, + *keybinding_checkbutton_any, *keybinding_checkbutton_control, *keybinding_checkbutton_shift, *keybinding_checkbutton_alt, *keybinding_checkbutton_meta, *keybinding_checkbutton_edit, *keybinding_entry_key, @@ -96,19 +97,17 @@ static int cur_history_position = 0, scroll_history_position = 0; /** * @{ - * @name Key Entry Struct + * @name key_entry struct * A keybinding hash record structure. */ -typedef struct Keys { +struct keybind { uint8 flags; /**< KEYF_* flags set for the record.*/ sint8 direction; /**< -1 non-direction key, else >= 0.*/ uint32 keysym; /**< Key this binding record is for. */ char *command; /**< Command string bound to a key. */ - struct Keys *next; -} Key_Entry; -/** - * @} EndOf Key Entry Struct - */ + struct keybind *next; +}; + /*********************************************************************** * @@ -128,20 +127,34 @@ static uint32 firekeysym[2], runkeysym[2], commandkeysym, *bind_keysym, static int bind_flags = 0; static char bind_buf[MAX_BUF]; -#define KEYF_NORMAL 0x01 /**< Used in normal mode */ -#define KEYF_FIRE 0x02 /**< Used in fire mode */ -#define KEYF_RUN 0x04 /**< Used in run mode */ -#define KEYF_EDIT 0x08 /**< Line editor */ -#define KEYF_STANDARD 0x10 /**< For standard/built-in keybinds */ -#define KEYF_ALT 0x20 /**< For ALT key modifier */ -#define KEYF_META 0x40 /**< For Meta key modifier */ -#define KEYF_MODIFIERS 0x67 /**< Mask for actual keyboard - * modifiers, not action modifiers */ +/* + * Key modifiers + * + * The Run, Fire, Alt and/or Meta keys can be used to qualify a key + * (i.e. a keybinding of the key 's' with the KEYF_RUN and KEYF_FIRE + * flags set will only match if both Run and Fire are held while 's' is + * pressed). + * + * If the user wants a key to match no matter the state of the modifier + * keys, the KEYF_ANY flag must be set in the binding. + */ + +#define KEYF_MOD_SHIFT (1 << 0) /**< Used in fire mode */ +#define KEYF_MOD_CTRL (1 << 1) /**< Used in run mode */ +#define KEYF_MOD_ALT (1 << 2) /**< For ALT key modifier */ +#define KEYF_MOD_META (1 << 3) /**< For Meta key modifier */ +#define KEYF_MOD_MASK (KEYF_MOD_SHIFT | \ + KEYF_MOD_CTRL | \ + KEYF_MOD_ALT | \ + KEYF_MOD_META) + +#define KEYF_ANY (1 << 4) /**< Don't care about modifiers */ +#define KEYF_EDIT (1 << 5) /**< Enter command mode */ extern const char *const directions[9]; #define KEYHASH 257 -static Key_Entry *keys[KEYHASH]; /**< Platform independence defines that +static struct keybind *keys[KEYHASH]; /**< Platform independence defines that * we can't use keycodes. instead, * make it a hash, and set KEYHASH to * a prime number for this purpose. @@ -152,6 +165,41 @@ static Key_Entry *keys[KEYHASH]; /**< Platform independence defines that * @{ */ +#define EKEYBIND_NOMEM 1 +#define EKEYBIND_TAKEN 2 + +/** + * Find a keybinding for keysym. + * + * Make it possible to match a specific keysym-and-key-modifier combo + * (useful in game play), or to match keysym regardless of modifier. + * + * @param flags If flags has got KEYF_ANY set, the keybinding's own + * flags are ignored and any match is returned. + * If a keybinding matching keysym which has got KEYF_ANY + * set is found, the flags param is ignored and the binding + * is returned. + * Otherwise, return only bindings with matching modifier + * flags. + */ +static struct keybind *keybind_find(uint32 keysym, unsigned int flags) +{ + struct keybind *kb; + + kb = keys[keysym % KEYHASH]; + while (kb != NULL) { + if (kb->keysym == 0 || kb->keysym == keysym) { + if ((kb->flags & KEYF_ANY) || (flags & KEYF_ANY)) + return kb; + if ((kb->flags & KEYF_MOD_MASK) == flags) + return kb; + } + kb = kb->next; + } + + return NULL; +} + /** * Updates the keys array with the keybinding that is passed. It allocates * memory for the array entry, then uses strdup_local() to allocate memory @@ -162,40 +210,70 @@ static Key_Entry *keys[KEYHASH]; /**< Platform independence defines that * @param flags State that the keyboard is in. * @param command A command to bind to the key specified in keysym. */ -static void insert_key(uint32 keysym, int flags, const char *command) +static int keybind_insert(uint32 keysym, unsigned int flags, const char *command) { - Key_Entry *newkey; - int i, direction = -1, slot; + struct keybind **next_ptr; + int slot; + int i; + int dir; + + if (keybind_find(keysym, flags) != NULL) + return -EKEYBIND_TAKEN; slot = keysym % KEYHASH; - if (keys[slot] == NULL) { - keys[slot] = malloc(sizeof(Key_Entry)); - keys[slot]->command = NULL; - keys[slot]->next = NULL; - newkey = keys[slot]; - } else { - newkey = keys[slot]; - while (newkey->next != NULL) { - newkey = newkey->next; - } - newkey->next = calloc(1, sizeof(Key_Entry)); - newkey = newkey->next; - } + next_ptr = &keys[slot]; + while (*next_ptr) + next_ptr = &(*next_ptr)->next; + *next_ptr = calloc(1, sizeof(**next_ptr)); + if (*next_ptr == NULL) + return -EKEYBIND_NOMEM; + + (*next_ptr)->keysym = keysym; + (*next_ptr)->flags = flags; + (*next_ptr)->command = strdup_local(command); + /* * Try to find out if the command is a direction command. If so, keep * track of this fact, so in fire or run mode, things work correctly. */ + dir = -1; for (i = 0; i < 9; i++) if (!strcmp(command, directions[i])) { - direction = i; + dir = i; break; } + (*next_ptr)->direction = dir; - newkey->keysym = keysym; - newkey->flags = flags; - newkey->command = strdup_local(command); - newkey->direction = direction; + return 0; +} + +static int keybind_remove(struct keybind *entry) +{ + struct keybind **next_ptr; + int slot; + + slot = entry->keysym % KEYHASH; + + next_ptr = &keys[slot]; + while (*next_ptr) { + if (*next_ptr == entry) { + *next_ptr = entry->next; + return 0; + } + next_ptr = &(*next_ptr)->next; + } + + /* No such key entry */ + return -1; +} + +static void keybind_free(struct keybind **entry) +{ + free((*entry)->command); + (*entry)->command = NULL; + free(*entry); + *entry = NULL; } /** @@ -205,11 +283,12 @@ static void insert_key(uint32 keysym, int flags, const char *command) * @param line * @param standard Set (1) or clear (0) the KEYF_STANDARD flag for the binding. */ -static void parse_keybind_line(char *buf, int line, int standard) +static void parse_keybind_line(char *buf, int line) { char *cp, *cpnext; uint32 keysym; int flags; + int res; /* * There may be a rare error case when cp is used uninitialized. So let's @@ -245,7 +324,7 @@ static void parse_keybind_line(char *buf, int line, int standard) "Line %d (%s) corrupted in keybinding file.", line, buf); return; } - *cp1 ++ = 0;/* Null terminate it */ + *cp1++ = 0; /* Null terminate it */ keysym = gdk_keyval_from_name(cp); /* As of now, all these keys must have keysyms */ if (keysym == 0) { @@ -302,12 +381,6 @@ static void parse_keybind_line(char *buf, int line, int standard) return; } } else { - if (standard) { - standard = KEYF_STANDARD; - } else { - standard = 0; - } - *cpnext++ = '\0'; keysym = gdk_keyval_from_name(buf); if (!keysym) { @@ -316,7 +389,7 @@ static void parse_keybind_line(char *buf, int line, int standard) return; } cp = cpnext; - cpnext = strchr(cp,' '); + cpnext = strchr(cp, ' '); if (cpnext == NULL) { LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", "Line %d (%s) corrupted in keybinding file.", line, cp); @@ -325,7 +398,7 @@ static void parse_keybind_line(char *buf, int line, int standard) *cpnext++ = '\0'; cp = cpnext; - cpnext = strchr(cp,' '); + cpnext = strchr(cp, ' '); if (cpnext == NULL) { LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", "Line %d (%s) corrupted in keybinding file.", line, cp); @@ -336,29 +409,25 @@ static void parse_keybind_line(char *buf, int line, int standard) while (*cp != '\0') { switch (*cp) { case 'A': - flags |= KEYF_NORMAL | KEYF_FIRE | KEYF_RUN - | KEYF_META | KEYF_ALT; + flags |= KEYF_ANY; break; case 'E': flags |= KEYF_EDIT; break; case 'F': - flags |= KEYF_FIRE; + flags |= KEYF_MOD_SHIFT; break; case 'L': /* A is used, so using L for alt */ - flags |= KEYF_ALT; + flags |= KEYF_MOD_ALT; break; case 'M': - flags |= KEYF_META; + flags |= KEYF_MOD_META; break; case 'N': - flags |= KEYF_NORMAL; + /* Nothing to do */ break; case 'R': - flags |= KEYF_RUN; - break; - case 'S': - flags |= KEYF_STANDARD; + flags |= KEYF_MOD_CTRL; break; default: LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", @@ -372,10 +441,15 @@ static void parse_keybind_line(char *buf, int line, int standard) cpnext[strlen(cpnext) - 1] = '\0'; if (strlen(cpnext) > (sizeof(bind_buf) - 1)) { cpnext[sizeof(bind_buf) - 1] = '\0'; - LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", "Command too long! Truncated."); + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Command too long! Truncated."); } - insert_key(keysym, flags | standard, cpnext); + res = keybind_insert(keysym, flags, cpnext); + if (res == -EKEYBIND_TAKEN) + LOG(LOG_WARNING, "gtk-v2::parse_keybind_line", + "Duplicate keybinds in file; ignoring binding of command \"%s\".", + cpnext); } /* else if not special binding line */ } @@ -387,11 +461,12 @@ static void init_default_keybindings(void) char buf[MAX_BUF]; int i; - LOG(LOG_DEBUG, "gtk-v2::init_default_keybindings", "Using built-in defaults"); + LOG(LOG_DEBUG, "gtk-v2::init_default_keybindings", + "Using built-in defaults"); for (i = 0; i < sizeof(def_keys) / sizeof(char *); i++) { strcpy(buf, def_keys[i]); - parse_keybind_line(buf, i, 1); + parse_keybind_line(buf, i); } } @@ -490,6 +565,8 @@ void keys_init(GtkWidget *window_root) keybinding_window = glade_xml_get_widget(dialog_xml, "keybinding_window"); xml_tree = glade_get_widget_tree(GTK_WIDGET(keybinding_window)); + keybinding_checkbutton_any = + glade_xml_get_widget(xml_tree, "keybinding_checkbutton_any"); keybinding_checkbutton_control = glade_xml_get_widget(xml_tree, "keybinding_checkbutton_control"); keybinding_checkbutton_shift = @@ -524,6 +601,9 @@ void keys_init(GtkWidget *window_root) g_signal_connect((gpointer) keybinding_button_bind, "clicked", G_CALLBACK(on_keybinding_button_bind_clicked), NULL); + g_signal_connect((gpointer) keybinding_checkbutton_any, "clicked", + G_CALLBACK(on_keybinding_checkbutton_any_clicked), NULL); + widget = glade_xml_get_widget(xml_tree, "keybinding_button_clear"); g_signal_connect((gpointer) widget, "clicked", G_CALLBACK(on_keybinding_button_clear_clicked), NULL); @@ -602,7 +682,7 @@ void keys_init(GtkWidget *window_root) while (fgets(buf, BIG_BUF, fp)) { line++; buf[BIG_BUF - 1] = '\0'; - parse_keybind_line(buf, line, 0); + parse_keybind_line(buf, line); } fclose(fp); @@ -620,7 +700,7 @@ void keys_init(GtkWidget *window_root) * * @param ks */ -static void parse_key_release(uint32 ks) +static void parse_key_release(uint32 keysym) { /* @@ -628,20 +708,20 @@ static void parse_key_release(uint32 ks) * Something smart does need to be done when the character enters a non * play mode with fire or run mode already set, however. */ - if (ks == firekeysym[0] || ks == firekeysym[1]) { + if (keysym == firekeysym[0] || keysym == firekeysym[1]) { cpl.fire_on = 0; clear_fire(); gtk_label_set(GTK_LABEL(fire_label), " "); - } else if (ks == runkeysym[0] || ks == runkeysym[1]) { + } else if (keysym == runkeysym[0] || keysym == runkeysym[1]) { cpl.run_on = 0; if (use_config[CONFIG_ECHO]) draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, "stop run"); clear_run(); gtk_label_set(GTK_LABEL(run_label), " "); - } else if (ks == altkeysym[0] || ks == altkeysym[1]) { + } else if (keysym == altkeysym[0] || keysym == altkeysym[1]) { cpl.alt_on = 0; - } else if (ks == metakeysym[0] || ks == metakeysym[1]) { + } else if (keysym == metakeysym[0] || keysym == metakeysym[1]) { cpl.meta_on = 0; } /* @@ -662,10 +742,13 @@ static void parse_key_release(uint32 ks) */ static void parse_key(char key, uint32 keysym) { - Key_Entry *keyentry, *first_match = NULL; + struct keybind *kb; int present_flags = 0; char buf[MAX_BUF], tmpbuf[MAX_BUF]; + /* We handle the Shift key separately */ + keysym = gdk_keyval_to_lower(keysym); + if (keysym == commandkeysym) { gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); gtk_entry_set_visibility(GTK_ENTRY(entry_commands), 1); @@ -692,42 +775,24 @@ static void parse_key(char key, uint32 keysym) return; } + present_flags = 0; if (cpl.run_on) { - present_flags |= KEYF_RUN; + present_flags |= KEYF_MOD_CTRL; } if (cpl.fire_on) { - present_flags |= KEYF_FIRE; + present_flags |= KEYF_MOD_SHIFT; } if (cpl.alt_on) { - present_flags |= KEYF_ALT; + present_flags |= KEYF_MOD_ALT; } if (cpl.meta_on) { - present_flags |= KEYF_META; - } - if (present_flags == 0) { - present_flags = KEYF_NORMAL; + present_flags |= KEYF_MOD_META; } - keyentry = keys[keysym % KEYHASH]; - while (keyentry != NULL) { - if ((keyentry->keysym != 0 && keyentry->keysym != keysym) || - (!(keyentry->flags & present_flags))) { - keyentry = keyentry->next; - continue; - } - first_match = keyentry; - - /* Try to find a prefect match */ - if ((keyentry->flags & KEYF_MODIFIERS) != present_flags) { - keyentry = keyentry->next; - continue; - } else { - break; - } - } - if (first_match != NULL) { - if (first_match->flags & KEYF_EDIT) { - strcpy(cpl.input_text, first_match->command); + kb = keybind_find(keysym, present_flags); + if (kb != NULL) { + if (kb->flags & KEYF_EDIT) { + strcpy(cpl.input_text, kb->command); cpl.input_state = Command_Mode; gtk_entry_set_text(GTK_ENTRY(entry_commands), cpl.input_text); gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); @@ -737,31 +802,33 @@ static void parse_key(char key, uint32 keysym) } /* Some spells (dimension door) need a valid count value */ - cpl.count = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinbutton_count)); + cpl.count = gtk_spin_button_get_value_as_int( + GTK_SPIN_BUTTON(spinbutton_count)); - if (first_match->direction >= 0) { + if (kb->direction >= 0) { if (cpl.fire_on) { - snprintf(buf, sizeof(buf), "fire %s", first_match->command); - fire_dir(first_match->direction); + snprintf(buf, sizeof(buf), "fire %s", kb->command); + fire_dir(kb->direction); } else if (cpl.run_on) { - run_dir(first_match->direction); - snprintf(buf, sizeof(buf), "run %s", first_match->command); + snprintf(buf, sizeof(buf), "run %s", kb->command); + run_dir(kb->direction); } else { - extended_command(first_match->command); + extended_command(kb->command); } if (use_config[CONFIG_ECHO]) draw_ext_info( NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, - first_match->command); + kb->command); } else { if (use_config[CONFIG_ECHO]) draw_ext_info( NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, - first_match->command); - extended_command(first_match->command); + kb->command); + extended_command(kb->command); } return; } + if (key >= '0' && key <= '9') { cpl.count = cpl.count*10 + (key - '0'); if (cpl.count > 100000) { @@ -772,20 +839,20 @@ static void parse_key(char key, uint32 keysym) } tmpbuf[0] = 0; if (cpl.fire_on) { - strcat(tmpbuf, "fire+"); + strcat(tmpbuf, "Shift + "); } if (cpl.run_on) { - strcat(tmpbuf, "run+"); + strcat(tmpbuf, "Ctrl + "); } if (cpl.alt_on) { - strcat(tmpbuf, "alt+"); + strcat(tmpbuf, "Alt + "); } if (cpl.meta_on) { - strcat(tmpbuf, "meta+"); + strcat(tmpbuf, "Meta + "); } snprintf(buf, sizeof(buf), - "Key %s%s is not bound to any command. Use bind to associate this keypress with a command", + "Key %s%s is not bound to any command. Use 'bind' to associate this keypress with a command", tmpbuf, keysym == NoSymbol ? "unknown" : gdk_keyval_name(keysym)); #ifdef WIN32 if ((65513 != keysym) && (65511 != keysym)) @@ -794,6 +861,37 @@ static void parse_key(char key, uint32 keysym) cpl.count = 0; } +static void get_key_modchars(struct keybind *kb, int save_mode, char *buf) +{ + int bi = 0; + + if (kb->flags & KEYF_ANY) { + buf[bi++] = 'A'; + } + if (save_mode || !(kb->flags & KEYF_ANY)) { + if ((kb->flags & KEYF_MOD_MASK) == 0) { + buf[bi++] = 'N'; + } + if (kb->flags & KEYF_MOD_SHIFT) { + buf[bi++] = 'F'; + } + if (kb->flags & KEYF_MOD_CTRL) { + buf[bi++] = 'R'; + } + if (kb->flags & KEYF_MOD_ALT) { + buf[bi++] = 'L'; + } + if (kb->flags & KEYF_MOD_META) { + buf[bi++] = 'M'; + } + } + if (kb->flags & KEYF_EDIT) { + buf[bi++] = 'E'; + } + + buf[bi] = '\0'; +} + /** * * @param key @@ -802,60 +900,34 @@ static void parse_key(char key, uint32 keysym) * information in a friendly manner. * @return A character string describing the key. */ -static char *get_key_info(Key_Entry *key, int save_mode) +static char *get_key_info(struct keybind *kb, int save_mode) { /* bind buf is the maximum space allowed for a - * binded command. We will add additional datas to - * it so we increase by MAX_BUF*/ + * bound command. We will add additional data to + * it so we increase its size by MAX_BUF*/ static char buf[MAX_BUF + sizeof(bind_buf)]; char buff[MAX_BUF]; - int bi = 0; - if ((key->flags & KEYF_MODIFIERS) == KEYF_MODIFIERS) { - buff[bi++] = 'A'; - } else { - if (key->flags & KEYF_NORMAL) { - buff[bi++] = 'N'; - } - if (key->flags & KEYF_FIRE) { - buff[bi++] = 'F'; - } - if (key->flags & KEYF_RUN) { - buff[bi++] = 'R'; - } - if (key->flags & KEYF_ALT) { - buff[bi++] = 'L'; - } - if (key->flags & KEYF_META) { - buff[bi++] = 'M'; - } - } - if (key->flags & KEYF_EDIT) { - buff[bi++] = 'E'; - } - if (key->flags & KEYF_STANDARD) { - buff[bi++] = 'S'; - } + get_key_modchars(kb, save_mode, buff); - buff[bi] = '\0'; if (save_mode) { - if (key->keysym == NoSymbol) { + if (kb->keysym == NoSymbol) { snprintf(buf, sizeof(buf), "(null) %i %s %s", - 0, buff, key->command); + 0, buff, kb->command); } else { snprintf(buf, sizeof(buf), "%s %i %s %s", - gdk_keyval_name(key->keysym), 0, - buff, key->command); + gdk_keyval_name(kb->keysym), + 0, buff, kb->command); } } else { - if (key->keysym == NoSymbol) { + if (kb->keysym == NoSymbol) { snprintf(buf, sizeof(buf), "key (null) %s %s", - buff, key->command); + buff, kb->command); } else { snprintf(buf, sizeof(buf), "key %s %s %s", - gdk_keyval_name(key->keysym), - buff, key->command); + gdk_keyval_name(kb->keysym), + buff, kb->command); } } return buf; @@ -866,10 +938,10 @@ static char *get_key_info(Key_Entry *key, int save_mode) * * @param allbindings Also shows the standard (default) keybindings. */ -static void show_keys(int allbindings) +static void show_keys(void) { int i, count = 1; - Key_Entry *key; + struct keybind *kb; char buf[MAX_BUF]; snprintf(buf, sizeof(buf), "Commandkey %s", @@ -912,12 +984,8 @@ static void show_keys(int allbindings) * Perhaps we should start at 8, so that we only show 'active' keybindings? */ for (i = 0; i < KEYHASH; i++) { - for (key = keys[i]; key != NULL; key = key->next) { - if (key->flags & KEYF_STANDARD && !allbindings) { - continue; - } - - snprintf(buf, sizeof(buf), "%3d %s", count, get_key_info(key, 0)); + for (kb = keys[i]; kb != NULL; kb = kb->next) { + snprintf(buf, sizeof(buf), "%3d %s", count, get_key_info(kb, 0)); draw_ext_info( NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); count++; @@ -939,9 +1007,10 @@ void bind_key(char *params) if (!params) { draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, - "Usage: bind [-aefmnr] {/commandkey/firekey{1/2}" - "/runkey{1/2}/altkey{1/2}/metakey{1/2}" - "completekey/nextkey/prevkey}"); +"Usage: 'bind -ed /commandkey/completekey/nextkey/prevkey'\n" +"Where\n" +" -e means enter edit mode\n" +" -d means don't care about modifier keys (key works no matter if Ctrl/Alt etc are held)"); return; } @@ -958,64 +1027,6 @@ void bind_key(char *params) return; } - if (!strcmp(params, "firekey1")) { - bind_keysym = &firekeysym[0]; - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, - "Push key to bind new firekey 1."); - cpl.input_state = Configure_Keys; - return; - } - if (!strcmp(params, "firekey2")) { - bind_keysym = &firekeysym[1]; - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, - "Push key to bind new firekey 2."); - cpl.input_state = Configure_Keys; - return; - } - if (!strcmp(params, "metakey1")) { - bind_keysym = &metakeysym[0]; - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, - "Push key to bind new metakey 1."); - cpl.input_state = Configure_Keys; - return; - } - if (!strcmp(params, "metakey2")) { - bind_keysym = &metakeysym[1]; - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, - "Push key to bind new metakey 2."); - cpl.input_state = Configure_Keys; - return; - } - if (!strcmp(params, "altkey1")) { - bind_keysym = &altkeysym[0]; - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, - "Push key to bind new altkey 1."); - cpl.input_state = Configure_Keys; - return; - } - if (!strcmp(params, "altkey2")) { - bind_keysym = &altkeysym[1]; - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, - "Push key to bind new altkey 2."); - cpl.input_state = Configure_Keys; - return; - } - if (!strcmp(params, "runkey1")) { - bind_keysym = &runkeysym[0]; - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, - "Push key to bind new runkey 1."); - cpl.input_state = Configure_Keys; - return; - } - - if (!strcmp(params, "runkey2")) { - bind_keysym = &runkeysym[1]; - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, - "Push key to bind new runkey 2."); - cpl.input_state = Configure_Keys; - return; - } - if (!strcmp(params, "completekey")) { bind_keysym = &completekeysym; draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, @@ -1039,31 +1050,17 @@ void bind_key(char *params) cpl.input_state = Configure_Keys; return; } + bind_keysym = NULL; - if (params[0] != '-') { - bind_flags = KEYF_MODIFIERS; - } else { - bind_flags = 0; - bind_keysym = NULL; + bind_flags = 0; + if (params[0] == '-') { for (params++; *params != ' '; params++) switch (*params) { - case 'a': - bind_flags |= KEYF_ALT; - break; case 'e': bind_flags |= KEYF_EDIT; break; - case 'f': - bind_flags |= KEYF_FIRE; - break; - case 'm': - bind_flags |= KEYF_META; - break; - case 'n': - bind_flags |= KEYF_NORMAL; - break; - case 'r': - bind_flags |= KEYF_RUN; + case 'd': + bind_flags |= KEYF_ANY; break; case '\0': draw_ext_info( @@ -1071,18 +1068,14 @@ void bind_key(char *params) "Use unbind to remove bindings."); return; default: - snprintf(buf, sizeof(buf), "Unsupported or invalid bind flag: '%c'", *params); - draw_ext_info( - NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_ERROR, buf); - return; + snprintf(buf, sizeof(buf), + "Unsupported or invalid bind flag: '%c'", *params); + draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_ERROR, buf); + return; } params++; } - if (!(bind_flags & KEYF_MODIFIERS)) { - bind_flags |= KEYF_MODIFIERS; - } - if (!params[0]) { draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, "Use unbind to remove bindings."); @@ -1115,13 +1108,12 @@ void bind_key(char *params) * returns when it becomes a NULL pointer. * @param kc */ -static void save_individual_key(FILE *fp, Key_Entry *key, KeyCode kc) +static void save_individual_key(FILE *fp, struct keybind *kb, KeyCode kc) { - if (key == NULL) { - return; + while (kb) { + fprintf(fp, "%s\n", get_key_info(kb, 1)); + kb = kb->next; } - fprintf(fp, "%s\n", get_key_info(key, 1)); - save_individual_key(fp, key->next, kc); } /** @@ -1235,11 +1227,15 @@ static void save_keys(void) static void configure_keys(uint32 keysym) { char buf[MAX_BUF]; - Key_Entry *keyentry, *first_match = NULL; + struct keybind *kb; + int res; + + /* We handle the Shift key separately */ + keysym = gdk_keyval_to_lower(keysym); /* * I think that basically if we are not rebinding the special control keys - * (in which case bind_kesym would be set to something) we just want to + * (in which case bind_keysym would be set to something) we just want to * handle these keypresses as normal events. */ if (bind_keysym == NULL) { @@ -1262,66 +1258,47 @@ static void configure_keys(uint32 keysym) return; } } - cpl.input_state = Playing; + /* - * Try to be clever - take into account shift/control keys being held down - * when binding keys - in this way, player does not have to use -f and -r - * flags to bind for many simple binds. + * Take shift/control keys into account when binding keys. */ - if ((cpl.fire_on || cpl.run_on || cpl.meta_on || cpl.alt_on) && - (bind_flags & KEYF_MODIFIERS) == KEYF_MODIFIERS) { - bind_flags &= ~KEYF_MODIFIERS; + if (!(bind_flags & KEYF_ANY)) { if (cpl.fire_on) { - bind_flags |= KEYF_FIRE; + bind_flags |= KEYF_MOD_SHIFT; } if (cpl.run_on) { - bind_flags |= KEYF_RUN; + bind_flags |= KEYF_MOD_CTRL; } if (cpl.meta_on) { - bind_flags |= KEYF_META; + bind_flags |= KEYF_MOD_META; } if (cpl.alt_on) { - bind_flags |= KEYF_ALT; + bind_flags |= KEYF_MOD_ALT; } } + /* Reset state now. We might return early if bind fails. */ + cpl.input_state = Playing; + if (bind_keysym != NULL) { *bind_keysym = keysym; bind_keysym = NULL; } else { - keyentry = keys[keysym % KEYHASH]; - while (keyentry != NULL) { - if ((keyentry->keysym != 0 && keyentry->keysym != keysym) || - (!(keyentry->flags & bind_flags))) { - keyentry = keyentry->next; - continue; - } - first_match = keyentry; - - /* Try to find a prefect match */ - if ((keyentry->flags & KEYF_MODIFIERS) != bind_flags) { - keyentry = keyentry->next; - continue; - } else { - break; - } - } - if (first_match) { + res = keybind_insert(keysym, bind_flags, bind_buf); + if (res == -EKEYBIND_TAKEN) { + kb = keybind_find(keysym, bind_flags); snprintf(buf, sizeof(buf), - "Warning: Keybind %s may conflict with new binding.", - first_match->command); + "Error: Key already used for command \"%s\". Use unbind first.", + kb->command); draw_ext_info( NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_ERROR, buf); + return; } - - insert_key(keysym, bind_flags, bind_buf); } - snprintf(buf, sizeof(buf), "Binded to key '%s' (%i)", + snprintf(buf, sizeof(buf), "Bound to key '%s' (%i)", keysym == NoSymbol ? "unknown" : gdk_keyval_name(keysym), keysym); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_CONFIG, buf); - cpl.fire_on = 0; - cpl.run_on = 0; draw_message_window(0); /* @@ -1339,13 +1316,9 @@ static void configure_keys(uint32 keysym) static void unbind_usage(void) { draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, - "Usage: unbind or"); - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, - "Usage: unbind [-a] [-g] to show existing bindings"); - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, - " -a shows all (global) bindings"); + "Usage: 'unbind ' or"); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, - " -g unbinds a global binding"); + "Usage: 'unbind' to show existing bindings"); } /** @@ -1354,12 +1327,13 @@ static void unbind_usage(void) */ void unbind_key(const char *params) { - int count = 0, keyentry, onkey,global = 0; - Key_Entry *key, *tmp; + int count = 0, keyentry, slot; + int res; + struct keybind *kb; char buf[MAX_BUF]; - if (params == NULL || params[0] == '\0') { - show_keys(0); + if (params == NULL) { + show_keys(); return; } @@ -1368,71 +1342,47 @@ void unbind_key(const char *params) params++; } - if (!strcmp(params, "-a")) { - show_keys(1); + if (params[0] == '\0') { + show_keys(); return; } - if (!strncmp(params, "-g", 2)) { - global = 1; - params = strchr(params,' '); - if (params == NULL) { - unbind_usage(); - return; - } - } - keyentry = atoi(params); - if (keyentry == 0) { + + if ((keyentry = atoi(params)) == 0) { unbind_usage(); return; } - for (onkey = 0; onkey < KEYHASH; onkey++) { - for (key = keys[onkey]; key; key = key->next) { - if (global || !(key->flags & KEYF_STANDARD)) { - count++; - } - /* We found the key we want to unbind */ - if (keyentry == count) { + for (slot = 0; slot < KEYHASH; slot++) { + for (kb = keys[slot]; kb; kb = kb->next) { + count++; - /* If it is the first entry, it is easy */ - if (key == keys[onkey]) { - keys[onkey] = key->next; - goto unbinded; - } - /* - * Otherwise, we need to figure out where in the link list the - * entry is. - */ - for (tmp = keys[onkey]; tmp->next != NULL; tmp = tmp->next) { - if (tmp->next == key) { - tmp->next = key->next; - goto unbinded; - } - } - LOG(LOG_ERROR, "gtk-v2::unbind_key", - "found number entry, but could not find actual key"); + if (keyentry == count) { + /* We found the key we want to unbind */ + snprintf(buf, sizeof(buf), "Removing binding: %3d %s", + count, get_key_info(kb, 0)); + draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, + MSG_TYPE_CLIENT_CONFIG, buf); + res = keybind_remove(kb); + if (res < 0) + LOG(LOG_ERROR, "gtk-v2::unbind_key", + "found number entry, but could not find actual key"); + keybind_free(&kb); + save_keys(); + return; } } } + + /* Not found */ /* Makes things look better to draw the blank line */ draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, ""); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, "Not found. Try 'unbind' with no options to find entry."); return; - /* - * Found. Now remove it. - */ -unbinded: - snprintf(buf, sizeof(buf), "Removed binding: %3d %s", count, get_key_info(key,0)); - - draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_CONFIG, buf); - free(key->command); - free(key); - save_keys(); } /** - * When the main window looses it's focus, act as if all keys have been released + * When the main window looses its focus, act as if all keys have been released */ void focusoutfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) { @@ -1626,63 +1576,28 @@ void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) void draw_keybindings(GtkWidget *keylist) { int i, count = 1; - Key_Entry *key; - int allbindings = 0; + struct keybind *kb; char buff[MAX_BUF]; - int bi = 0; char buffer[5][MAX_BUF]; char *buffers[5]; - gint tmprow; - gtk_clist_clear (GTK_CLIST(keylist)); + gtk_clist_clear(GTK_CLIST(keylist)); for (i = 0; i < KEYHASH; i++) { - for (key = keys[i]; key != NULL; key = key->next) { - if (key->flags & KEYF_STANDARD && !allbindings) { - continue; - } - - bi = 0; - - if ((key->flags & KEYF_MODIFIERS) == KEYF_MODIFIERS) { - buff[bi++] = 'A'; - } else { - if (key->flags & KEYF_NORMAL) { - buff[bi++] = 'N'; - } - if (key->flags & KEYF_FIRE) { - buff[bi++] = 'F'; - } - if (key->flags & KEYF_RUN) { - buff[bi++] = 'R'; - } - if (key->flags & KEYF_ALT) { - buff[bi++] = 'L'; - } - if (key->flags & KEYF_META) { - buff[bi++] = 'M'; - } - } - if (key->flags & KEYF_EDIT) { - buff[bi++] = 'E'; - } - if (key->flags & KEYF_STANDARD) { - buff[bi++] = 'S'; - } - - buff[bi] = '\0'; + for (kb = keys[i]; kb != NULL; kb = kb->next) { + get_key_modchars(kb, 0, buff); - if (key->keysym != NoSymbol) { + if (kb->keysym != NoSymbol) { snprintf(buffer[0], sizeof(buffer[0]), "%i", count); - snprintf(buffer[1], sizeof(buffer[1]), "%s", gdk_keyval_name(key->keysym)); + snprintf(buffer[1], sizeof(buffer[1]), "%s", gdk_keyval_name(kb->keysym)); snprintf(buffer[2], sizeof(buffer[2]), "%i", i); snprintf(buffer[3], sizeof(buffer[3]), "%s", buff); - snprintf(buffer[4], sizeof(buffer[4]), "%s", key->command); + snprintf(buffer[4], sizeof(buffer[4]), "%s", kb->command); buffers[0] = buffer[0]; buffers[1] = buffer[1]; buffers[2] = buffer[2]; buffers[3] = buffer[3]; buffers[4] = buffer[4]; - tmprow = gtk_clist_append(GTK_CLIST(keylist), buffers); + gtk_clist_append(GTK_CLIST(keylist), buffers); } count++; } @@ -1736,7 +1651,7 @@ void gtk_command_history(int direction) */ gtk_entry_set_text(GTK_ENTRY(entry_commands), ""); gtk_entry_set_position(GTK_ENTRY(entry_commands), 0); - scroll_history_position=cur_history_position; + scroll_history_position = cur_history_position; return; } } @@ -1746,7 +1661,7 @@ void gtk_command_history(int direction) } scroll_history_position=i; - /* fprintf(stderr,"resetting postion to %d, data = %s\n", i, history[i]);*/ + /* fprintf(stderr, "resetting postion to %d, data = %s\n", i, history[i]);*/ gtk_entry_set_text(GTK_ENTRY(entry_commands), history[i]); gtk_widget_grab_focus(GTK_WIDGET(entry_commands)); gtk_editable_select_region(GTK_EDITABLE(entry_commands), 0, 0); @@ -1855,48 +1770,49 @@ void on_entry_commands_activate(GtkEntry *entry, gpointer user_data) */ void update_keybinding_list(void) { - int i, allbindings = 0; - Key_Entry *key; + int i; + struct keybind *kb; char modifier_buf[256]; GtkTreeIter iter; gtk_list_store_clear(keybinding_store); for (i = 0; i < KEYHASH; i++) { - for (key = keys[i]; key != NULL; key = key->next) { - if (key->flags & KEYF_STANDARD && !allbindings) { - continue; - } - + for (kb = keys[i]; kb != NULL; kb = kb->next) { modifier_buf[0] = 0; - if ((key->flags & KEYF_MODIFIERS) != KEYF_MODIFIERS) { - if (key->flags & KEYF_ALT) { - strcat(modifier_buf, "Alt "); + if (kb->flags & KEYF_ANY) { + strcat(modifier_buf, "Any"); + } else if ((kb->flags & KEYF_MOD_MASK) == 0) { + strcat(modifier_buf, "None"); + } else { + if (kb->flags & KEYF_MOD_ALT) { + strcat(modifier_buf, "Alt"); + if (kb->flags & (KEYF_MOD_SHIFT | KEYF_MOD_CTRL | KEYF_MOD_META)) + strcat(modifier_buf, " + "); } - if (key->flags & KEYF_FIRE) { - strcat(modifier_buf, "Fire "); + if (kb->flags & KEYF_MOD_SHIFT) { + strcat(modifier_buf, "Shift"); + if (kb->flags & (KEYF_MOD_CTRL | KEYF_MOD_META)) + strcat(modifier_buf, " + "); } - if (key->flags & KEYF_RUN) { - strcat(modifier_buf, "Run "); + if (kb->flags & KEYF_MOD_CTRL) { + strcat(modifier_buf, "Ctrl"); + if (kb->flags & KEYF_MOD_META) + strcat(modifier_buf, " + "); } - if (key->flags & KEYF_META) { - strcat(modifier_buf, "Meta "); + if (kb->flags & KEYF_MOD_META) { + strcat(modifier_buf, "Meta"); } - } else { - strcat(modifier_buf, "All "); - } - if (key->flags & KEYF_STANDARD) { - strcat(modifier_buf, "(Standard) "); } gtk_list_store_append(keybinding_store, &iter); gtk_list_store_set(keybinding_store, &iter, KLIST_ENTRY, i, - KLIST_KEY, gdk_keyval_name(key->keysym), + KLIST_KEY, gdk_keyval_name(kb->keysym), KLIST_MODS, modifier_buf, - KLIST_EDIT, (key->flags & KEYF_EDIT) ? "Yes" : "No", - KLIST_COMMAND, key->command, - KLIST_KEY_ENTRY, key, + KLIST_EDIT, (kb->flags & KEYF_EDIT) ? "Yes":"No", + KLIST_COMMAND, kb->command, + KLIST_KEY_ENTRY, kb, -1); } } @@ -1927,8 +1843,10 @@ void on_keybindings_activate(GtkMenuItem *menuitem, gpointer user_data) * @param user_data * @return TRUE (Returning TRUE prevents widget from getting this event.) */ -gboolean on_keybinding_entry_key_key_press_event(GtkWidget *widget, - GdkEventKey *event, gpointer user_data) +gboolean +on_keybinding_entry_key_key_press_event(GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) { gtk_entry_set_text( GTK_ENTRY(keybinding_entry_key), gdk_keyval_name(event->keyval)); @@ -1984,52 +1902,28 @@ void on_keybinding_button_remove_clicked(GtkButton *button, gpointer user_data) { GtkTreeModel *model; GtkTreeIter iter; - Key_Entry *entry, *key, *tmp; - int onkey; + struct keybind *kb; + int res; if (!gtk_tree_selection_get_selected(keybinding_selection, &model, &iter)) { LOG(LOG_ERROR, "keys.c::on_keybinding_button_remove_clicked", "Function called with nothing selected\n"); return; } - gtk_tree_model_get(model, &iter, KLIST_KEY_ENTRY, &entry, -1); - for (onkey = 0; onkey < KEYHASH; onkey++) { - for (key = keys[onkey]; key; key = key->next) { - if (key == entry) { - /* - * This code is directly from unbind_key() above If it is the - * first entry, it is easy - */ - if (key == keys[onkey]) { - keys[onkey] = key->next; - goto unbinded; - } - /* - * Otherwise, we need to figure out where in the link list the - * entry is. - */ - for (tmp = keys[onkey]; tmp->next != NULL; tmp = tmp->next) { - if (tmp->next == key) { - tmp->next = key->next; - goto unbinded; - } - } - } - } - } - LOG(LOG_ERROR, "keys.c::on_keybinding_button_remove_clicked", - "Unable to find matching key entry\n"); + gtk_tree_model_get(model, &iter, KLIST_KEY_ENTRY, &kb, -1); + res = keybind_remove(kb); + if (res < 0) + LOG(LOG_ERROR, "keys.c::on_keybinding_button_remove_clicked", + "Unable to find matching key entry\n"); + keybind_free(&kb); -unbinded: - free(key->command); - free(key); save_keys(); update_keybinding_list(); } /** - * Gets the state information from what checkboxes and other data in the window - * and puts it in the variables passed passed. This is used by both the update + * Gets the state information from checkboxes and other data in the window + * and puts it in the variables passed. This is used by both the update * and add functions. * * @param keysym @@ -2043,33 +1937,25 @@ static void keybinding_get_data(uint32 *keysym, uint8 *flags, const char **comma *flags = 0; if (gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(keybinding_checkbutton_any))) { + *flags |= KEYF_ANY; + } + if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_control))) { - *flags |= KEYF_RUN; + *flags |= KEYF_MOD_CTRL; } - if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_shift))) { - *flags |= KEYF_FIRE; + *flags |= KEYF_MOD_SHIFT; } - if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_alt))) { - *flags |= KEYF_ALT; + *flags |= KEYF_MOD_ALT; } - if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_meta))) { - *flags |= KEYF_META; + *flags |= KEYF_MOD_META; } - - /* Set the KEYF_NORMAL flag when either none or all of the Run, Fire, Alt, - * Meta checkboxes are checked. This closely matches the combined existing - * logic in the parse_key() and get_key_info() functions. - */ - if (!*flags || (*flags | KEYF_NORMAL) == KEYF_MODIFIERS) { - *flags |= KEYF_NORMAL; - } - if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_edit))) { *flags |= KEYF_EDIT; @@ -2110,11 +1996,16 @@ void on_keybinding_button_bind_clicked(GtkButton *button, gpointer user_data) uint32 keysym; uint8 flags; const char *command; + int res; keybinding_get_data(&keysym, &flags, &command); - /* insert_key will do a strdup of command for us */ - insert_key(keysym, flags, command); + /* keybind_insert will do a strdup of command for us */ + res = keybind_insert(keysym, flags, command); + if (res == -EKEYBIND_TAKEN) { + // FIXME: popup message key already in use? + return; + } /* * I think it is more appropriate to clear the fields once the user adds @@ -2138,21 +2029,37 @@ void on_keybinding_button_bind_clicked(GtkButton *button, gpointer user_data) void on_keybinding_button_update_clicked(GtkButton *button, gpointer user_data) { GtkTreeIter iter; - Key_Entry *entry; + struct keybind *kb; GtkTreeModel *model; + uint32 keysym; + uint8 flags; const char *buf; + int res; if (gtk_tree_selection_get_selected(keybinding_selection, &model, &iter)) { - gtk_tree_model_get(model, &iter, KLIST_KEY_ENTRY, &entry, -1); + gtk_tree_model_get(model, &iter, KLIST_KEY_ENTRY, &kb, -1); - if (!entry) { + if (!kb) { LOG(LOG_ERROR, "keys.c::on_keybinding_button_update_clicked", "Unable to get key_entry structure\n"); return; } - free(entry->command); - keybinding_get_data(&entry->keysym, &entry->flags, &buf); - entry->command = strdup_local(buf); + + /* We need to rehash the binding (remove the old and add the + * new) since the keysym might have changed. */ + + keybind_remove(kb); + + keybinding_get_data(&keysym, &flags, &buf); + res = keybind_insert(keysym, flags, buf); + if (res == 0) { + keybind_free(&kb); + } else { + /* Re-enter old binding if the update failed */ + keybind_insert(kb->keysym, kb->flags, kb->command); + // FIXME: Popup dialog key in use + } + update_keybinding_list(); save_keys(); } else { @@ -2173,6 +2080,24 @@ void on_keybinding_button_close_clicked(GtkButton *button, gpointer user_data) } /** + * Deactivate the modifier checkboxes if "Any" is selected. + * + * @param button + * @param user_data + */ +void on_keybinding_checkbutton_any_clicked(GtkCheckButton *cb, gpointer user_data) +{ + gboolean enabled; + + enabled = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb)); + + gtk_widget_set_sensitive(GTK_WIDGET(keybinding_checkbutton_control), enabled); + gtk_widget_set_sensitive(GTK_WIDGET(keybinding_checkbutton_shift), enabled); + gtk_widget_set_sensitive(GTK_WIDGET(keybinding_checkbutton_alt), enabled); + gtk_widget_set_sensitive(GTK_WIDGET(keybinding_checkbutton_meta), enabled); +} + +/** * Called when the user clicks one of the entries in the list of keybindings * and places information about it into the input fields on the dialog. This * allows the player to edit and update, or remove bindings. @@ -2192,49 +2117,56 @@ gboolean keybinding_selection_func( gpointer userdata) { GtkTreeIter iter; - Key_Entry *entry; + struct keybind *kb; gtk_widget_set_sensitive(keybinding_button_remove, TRUE); gtk_widget_set_sensitive(keybinding_button_update, TRUE); if (gtk_tree_model_get_iter(model, &iter, path)) { - gtk_tree_model_get(model, &iter, KLIST_KEY_ENTRY, &entry, -1); + gtk_tree_model_get(model, &iter, KLIST_KEY_ENTRY, &kb, -1); - if (!entry) { + if (!kb) { LOG(LOG_ERROR, "keys.c::keybinding_selection_func", "Unable to get key_entry structure\n"); return FALSE; } - if (entry->flags & KEYF_RUN) + if (kb->flags & KEYF_ANY) + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(keybinding_checkbutton_any), TRUE); + else + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(keybinding_checkbutton_any), FALSE); + + if (kb->flags & KEYF_MOD_CTRL) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_control), TRUE); else gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_control), FALSE); - if (entry->flags & KEYF_FIRE) + if (kb->flags & KEYF_MOD_SHIFT) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_shift), TRUE); else gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_shift), FALSE); - if (entry->flags & KEYF_ALT) + if (kb->flags & KEYF_MOD_ALT) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_alt), TRUE); else gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_alt), FALSE); - if (entry->flags & KEYF_META) + if (kb->flags & KEYF_MOD_META) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_meta), TRUE); else gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_meta), FALSE); - if (entry->flags & KEYF_EDIT) + if (kb->flags & KEYF_EDIT) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(keybinding_checkbutton_edit), TRUE); else @@ -2242,9 +2174,9 @@ gboolean keybinding_selection_func( GTK_TOGGLE_BUTTON(keybinding_checkbutton_edit), FALSE); gtk_entry_set_text( - GTK_ENTRY(keybinding_entry_key), gdk_keyval_name(entry->keysym)); + GTK_ENTRY(keybinding_entry_key), gdk_keyval_name(kb->keysym)); gtk_entry_set_text( - GTK_ENTRY(keybinding_entry_command), entry->command); + GTK_ENTRY(keybinding_entry_command), kb->command); } return TRUE; } -- 1.8.1.5 -- Arvid From arvidb at kth.se Mon Oct 28 18:20:49 2013 From: arvidb at kth.se (Arvid Brodin) Date: Tue, 29 Oct 2013 00:20:49 +0100 Subject: [crossfire] [RFC 3/3] character-specific keybinding files Message-ID: <526EF151.9070402@kth.se> As the Subject says. Also removes the weird MULTKEYS definition... Signed-off-by: Arvid Brodin --- common/client.h | 13 ++-- common/metaserver.c | 4 -- gtk-v2/src/account.c | 5 ++ gtk-v2/src/create_char.c | 4 ++ gtk-v2/src/gtk2proto.h | 1 + gtk-v2/src/keys.c | 165 ++++++++++++++++++++++++----------------------- 6 files changed, 100 insertions(+), 92 deletions(-) diff --git a/common/client.h b/common/client.h index 75d3a54..cb0779a 100644 --- a/common/client.h +++ b/common/client.h @@ -40,8 +40,6 @@ # include #endif -#define MULTKEYS - #define VERSION_CS 1023 #define VERSION_SC 1029 @@ -349,10 +347,13 @@ typedef struct Player_Struct { uint16 mapxres,mapyres; /**< Resolution to draw on the magic * map. Only used in client-specific * code, so it should move there. */ -#ifdef MULTKEYS - char name[ 40 ]; /**< Player's name, for player-specific - * key files */ -#endif + char *name; /**< Name of PC, set and freed in account.c + * play_character() (using data returned + * from server to AccountPlayersCmd, via + * character_choose window, + * OR in + * send_create_player_to_server() when + * new character created. */ } Client_Player; /** diff --git a/common/metaserver.c b/common/metaserver.c index 3170335..2d68972 100644 --- a/common/metaserver.c +++ b/common/metaserver.c @@ -1041,11 +1041,7 @@ int metaserver_select(char *sel) snprintf(buf, sizeof(buf), "Trying to connect to %s:%d", server_name, port); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_METASERVER, buf); -#ifdef MULTKEYS csocket.fd = init_connection(server_name, port); -#else - csocket.fd = init_connection(server_ip, port); -#endif if (csocket.fd == -1) { draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_METASERVER, "Unable to connect to server."); diff --git a/gtk-v2/src/account.c b/gtk-v2/src/account.c index 0ed45b8..59c4d64 100644 --- a/gtk-v2/src/account.c +++ b/gtk-v2/src/account.c @@ -502,6 +502,11 @@ static void play_character(const char *name) SockList_AddString(&sl, "accountplay "); SockList_AddString(&sl, name); SockList_Send(&sl, csocket.fd); + + if (cpl.name) + free(cpl.name); + cpl.name = strdup(name); + keybindings_init(); } /** diff --git a/gtk-v2/src/create_char.c b/gtk-v2/src/create_char.c index 567d11a..ef6e709 100644 --- a/gtk-v2/src/create_char.c +++ b/gtk-v2/src/create_char.c @@ -357,6 +357,10 @@ static void send_create_player_to_server() SockList_Send(&sl, csocket.fd); + if (cpl.name) + free(cpl.name); + cpl.name = strdup(char_name); + keybindings_init(); } diff --git a/gtk-v2/src/gtk2proto.h b/gtk-v2/src/gtk2proto.h index 26f7c6e..0e894a5 100644 --- a/gtk-v2/src/gtk2proto.h +++ b/gtk-v2/src/gtk2proto.h @@ -112,6 +112,7 @@ extern void animate_inventory(void); extern void animate_look(void); extern void inventory_tick(void); /* keys.c */ +extern void keybindings_init(); extern void keys_init(GtkWidget *window_root); extern void bind_key(char *params); extern void unbind_key(const char *params); diff --git a/gtk-v2/src/keys.c b/gtk-v2/src/keys.c index fa91675..7b2ee61 100644 --- a/gtk-v2/src/keys.c +++ b/gtk-v2/src/keys.c @@ -470,24 +470,40 @@ static void init_default_keybindings(void) } } +static int parse_keys_file(char *filename) +{ + int line = 0; + FILE *fp; + char buf[BIG_BUF]; + + CONVERT_FILESPEC_TO_OS_FORMAT(filename); + LOG(LOG_INFO, "gtk-v2::init_keys", + "Trying to open keybinding file %s", filename); + + fp = fopen(filename, "r"); + if (fp == NULL) + return -1; + + while (fgets(buf, BIG_BUF, fp)) { + line++; + buf[BIG_BUF - 1] = '\0'; + parse_keybind_line(buf, line); + } + + fclose(fp); + return 0; +} + /** - * Reads in the keybindings, and initializes special values. It is called + * Reads in the keybindings, and initializes special values. Called * from main() as part of the client start up. The function is common to both * the x11 and gdk clients. - * - * @param window_root The client's main window. - * - * @todo Fix the per-character keys file support that is under \#if 0. */ -void keys_init(GtkWidget *window_root) +void keybindings_init() { - int i, line = 0; - FILE *fp; + int i; char buf[BIG_BUF]; - GtkTreeViewColumn *column; - GtkCellRenderer *renderer; - GladeXML *xml_tree; - GtkWidget *widget; + int res; for (i = 0; i < MAX_HISTORY; i++) { /* Clear out the bind history log */ history[i][0] = 0; @@ -516,7 +532,8 @@ void keys_init(GtkWidget *window_root) prevkeysym = NoSymbol; for (i = 0; i < KEYHASH; i++) { - keys[i] = NULL; + while (keys[i]) + keybind_remove(keys[i]); } /* @@ -528,29 +545,44 @@ void keys_init(GtkWidget *window_root) * the same as what it was in the server distribution. To convert bindings * in character files to this format, all that needs to be done is remove * the 'key ' at the start of each line. - * - * We need at least one of these keybinding files to exist - this is where - * the various commands are defined. In theory, we actually don't need to - * have any of these defined -- the player could just bind everything. - * Probably not a good idea, however. */ -#if 0 - /* For Windows, use player name if defined for key file */ - /* FIXME: keys_init() is called long before the player logs in, so until - * that is fixed, it is pointless to have this code check for cpl.name - * being set. Also, it is completely inappropriate for this to be a - * Windows only feature. - */ - if ( strlen( cpl.name ) ) { - sprintf( buf, "%s/.crossfire/%s.keys", getenv( "HOME" ), cpl.name ); - } else { - sprintf(buf,"%s/.crossfire/keys", getenv("HOME")); + /* Try the character-specific keys file */ + snprintf(buf, sizeof(buf), "%s/.crossfire/%s.keys", getenv("HOME"), cpl.name); + res = parse_keys_file(buf); + if (res < 0) { + /* Try the user-specific keys file */ + snprintf(buf, sizeof(buf), "%s/.crossfire/keys", getenv("HOME")); + res = parse_keys_file(buf); } -#else - snprintf(buf, sizeof(buf), "%s/.crossfire/keys", getenv("HOME")); - CONVERT_FILESPEC_TO_OS_FORMAT(buf); -#endif + if ((res < 0) && (client_libdir != NULL)) { + /* Try the installation-specific keys file */ + snprintf(buf, sizeof(buf), "%s/def_keys", client_libdir); + res = parse_keys_file(buf); + } + if (res < 0) { + /* Use built-in defaults */ + LOG(LOG_INFO, "gtk-v2::init_keys", + "Could not open any keybindings file; using defaults"); + init_default_keybindings(); + } +} + + +/** + * One-time initialization of windows and signals for the keybindings + * dialog. It is called from main() as part of the client start up. The + * function is common to both the x11 and gdk clients. + * + * @param window_root The client's main window. + */ +void keys_init(GtkWidget *window_root) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GladeXML *xml_tree; + GtkWidget *widget; + int i; xml_tree = glade_get_widget_tree(GTK_WIDGET(window_root)); @@ -658,34 +690,8 @@ void keys_init(GtkWidget *window_root) KLIST_KEY, GTK_SORT_ASCENDING); - /* Try to read user keybindings and load defaults if that fails. */ - fp = fopen(buf, "r"); - if (fp == NULL) { - LOG(LOG_INFO, "gtk-v2::init_keys", - "Could not open user keybindings; using defaults"); - - /* Use built-in defaults if there is no system directory. */ - if (client_libdir == NULL) { - init_default_keybindings(); - return; - } - - /* Try to read system keybindings before using built-in defaults. */ - snprintf(buf, sizeof(buf), "%s/def_keys", client_libdir); - fp = fopen(buf, "r"); - if (fp == NULL) { - init_default_keybindings(); - return; - } - } - - while (fgets(buf, BIG_BUF, fp)) { - line++; - buf[BIG_BUF - 1] = '\0'; - parse_keybind_line(buf, line); - } - - fclose(fp); + for (i = 0; i < KEYHASH; i++) + keys[i] = NULL; } /** @@ -1122,8 +1128,6 @@ static void save_individual_key(FILE *fp, struct keybind *kb, KeyCode kc) * Next, the entire key hash is traversed and the contents of each slot is * dumped to the file, and the output file is closed. Success or failure is * reported to the message pane. - * - * @todo Fix the per-character keys file support that is under \#if 0. */ static void save_keys(void) { @@ -1131,31 +1135,15 @@ static void save_keys(void) int i; FILE *fp; -#if 0 - /* Use player's name if available */ - /* FIXME: keys_init() is called long before the player logs in, so until - * that is fixed, it is pointless to have this code check for cpl.name - * being set so that a file is written that cannot be opened by under - * the existing code structure. That just means the keybindings saved - * while logged in would be inaccessible until the file was copied to - * the regular keys file. Also, this was originally under #ifdef WIN32, - * but is completely inappropriate for this to be a Windows only feature. - */ - if ( strlen( cpl.name ) ) { - sprintf( buf,"%s/.crossfire/%s.keys", getenv("HOME"), cpl.name ); - } else { - sprintf( buf,"%s/.crossfire/keys", getenv("HOME") ); - } -#else - snprintf(buf, sizeof(buf), "%s/.crossfire/keys", getenv("HOME")); + snprintf(buf, sizeof(buf), "%s/.crossfire/%s.keys", getenv("HOME"), cpl.name); CONVERT_FILESPEC_TO_OS_FORMAT(buf); -#endif + LOG(LOG_WARNING, "gtk-v2::save_keys", "Saving keybindings to %s", buf); if (make_path_to_file(buf) == -1) { LOG(LOG_WARNING, "gtk-v2::save_keys", "Could not create %s", buf); return; } - fp = fopen(buf,"w"); + fp = fopen(buf, "w"); if (fp == NULL) { snprintf(buf2, sizeof(buf2), "Could not open %s, key bindings not saved\n", buf); draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_ERROR, buf2); -- 1.8.1.5 -- Arvid From kevinz5000 at gmail.com Mon Oct 28 18:46:23 2013 From: kevinz5000 at gmail.com (Kevin Zheng) Date: Mon, 28 Oct 2013 18:46:23 -0500 Subject: [crossfire] [RFC 0/3] Work on keys.c and the keybinding system In-Reply-To: <526EEEB3.6030106@kth.se> References: <526EEEB3.6030106@kth.se> Message-ID: <526EF74F.5050103@gmail.com> Hi Arvid, Thank you very much for your contributions, it's really appreciated! I'm glad you enjoy playing Crossfire as much as you want to improve it. On 10/28/2013 18:09, Arvid Brodin wrote: > I'm thinking that perhaps I should also change the flags in the keys file so > that they conform with the modifier keys? Originally they were: > > [A]ll, [N]ormal, [F]ire, [R]un, A[L]t, [M]eta, [E]dit. > > Perhaps > > [A]ny, [N]one, [S]hift, [C]trl, A[L]t, [M]eta, [E]dit. This seems to me like a good idea. Since they won't coexist with the old key binding format, it should be perfectly fine. Maybe overhauling the entire file format isn't a bad idea, either. > Maybe also change the -d flag of 'bind' to '-i' (ignore)? Or '-a' (but -a meant > Alt before). The command-line bind command has been quite messy. I'm fairly certain that most people use the key binding dialog, so I think changing it wouldn't be a problem. > Looking forward, I would like to remove the fire_on and run_on "hacks" and > instead use a generic way to handle repeating keys. Then one would bind e.g. The underlying issue may also be in the server protocol. I don't remember very well right now, I'll have to take a look at the code. I'm fairly used to the CTRL-RUN combination, but realize that a press-and-hold solution is more intuitive. If you can get both to work I'll be sold. A final nitpick: sending patches as MIME attachments is much easier to work with. Is the mailing list scrubbing them and putting them inline? Thanks, Kevin Zheng From arvidb at kth.se Mon Oct 28 21:20:13 2013 From: arvidb at kth.se (Arvid Brodin) Date: Tue, 29 Oct 2013 03:20:13 +0100 Subject: [crossfire] [RFC 0/3] Work on keys.c and the keybinding system In-Reply-To: <526EF74F.5050103@gmail.com> References: <526EEEB3.6030106@kth.se> <526EF74F.5050103@gmail.com> Message-ID: <526F1B5D.7070908@kth.se> On 2013-10-29 00:46, Kevin Zheng wrote: > Hi Arvid, > > Thank you very much for your contributions, it's really appreciated! I'm > glad you enjoy playing Crossfire as much as you want to improve it. I'm glad to hear it! I know the patches are large; I hope they can get reviewed and ultimately accepted anyway. > On 10/28/2013 18:09, Arvid Brodin wrote: >> I'm thinking that perhaps I should also change the flags in the keys file so >> that they conform with the modifier keys? Originally they were: >> >> [A]ll, [N]ormal, [F]ire, [R]un, A[L]t, [M]eta, [E]dit. >> >> Perhaps >> >> [A]ny, [N]one, [S]hift, [C]trl, A[L]t, [M]eta, [E]dit. > > This seems to me like a good idea. Since they won't coexist with the old > key binding format, it should be perfectly fine. Maybe overhauling the > entire file format isn't a bad idea, either. Any ideas for what needs to be done there? I haven't had any issues with the file format as such, but if there are known problems, now would of course be a good opportunity to deal with them. >> Maybe also change the -d flag of 'bind' to '-i' (ignore)? Or '-a' (but -a meant >> Alt before). > > The command-line bind command has been quite messy. I'm fairly certain > that most people use the key binding dialog, so I think changing it > wouldn't be a problem. > >> Looking forward, I would like to remove the fire_on and run_on "hacks" and >> instead use a generic way to handle repeating keys. Then one would bind e.g. > > The underlying issue may also be in the server protocol. I don't > remember very well right now, I'll have to take a look at the code. I'm > fairly used to the CTRL-RUN combination, but realize that a > press-and-hold solution is more intuitive. If you can get both to work > I'll be sold. > > A final nitpick: sending patches as MIME attachments is much easier to > work with. Is the mailing list scrubbing them and putting them inline? Ah. :) No, I sent them inline. I've mainly worked on the Linux kernel, where you are required to send patches inline so that people can review them and comment on specific parts of the code, if necessary. But then the busiest maintainers there review several thousand patches each year, so I guess every little bit helps. And they've got the get-the-patches-from-mails-and- apply-them part automated using scripts, I believe. I'll send further patches as attachments if you want me to, but you should be able to apply these ones by just copying the entire mail text (beginning comments and everything) and paste it into any editor that doesn't strip whitespace from line ends or break lines (e.g. 'nano -w'). I did a trial run of the first two patches and they apply without problems. If you use Thunderbird there's a "Save as..." menu item for saving the mail as "eml" which works as well. (Just change the file name to something sane.) Maybe other mail readers have similar options. > > Thanks, > Kevin Zheng > _______________________________________________ > crossfire mailing list > crossfire at metalforge.org > http://mailman.metalforge.org/mailman/listinfo/crossfire -- Arvid From kevinz5000 at gmail.com Wed Oct 30 18:04:44 2013 From: kevinz5000 at gmail.com (Kevin Zheng) Date: Wed, 30 Oct 2013 18:04:44 -0500 Subject: [crossfire] [RFC 1/3] keys.c: Style fixes In-Reply-To: <526EF012.2070809@kth.se> References: <526EF012.2070809@kth.se> Message-ID: <5271908C.6030205@gmail.com> On 10/28/2013 18:15, Arvid Brodin wrote: > Use consistent coding style throughout the file. Committed in r19089, with minor changes. Thanks! Thanks, Kevin Zheng From kevinz5000 at gmail.com Wed Oct 30 18:34:37 2013 From: kevinz5000 at gmail.com (Kevin Zheng) Date: Wed, 30 Oct 2013 18:34:37 -0500 Subject: [crossfire] [RFC 2/3] Misc keybinding fixes and changes In-Reply-To: <526EF0F2.7000208@kth.se> References: <526EF0F2.7000208@kth.se> Message-ID: <5271978D.8000701@gmail.com> Due to a minor change (addition of braces in a 'for' loop) in the previous patch (RFC 1/3), the original patch no longer applies cleanly. I've attached a resolved patch that should apply correctly now. Please tell me if something doesn't look right. Sorry for the confusion. On 10/28/2013 18:19, Arvid Brodin wrote: > * Add "Any" key modifier type; New "Any" button in keybindings dialog. This lets > the user choose between having a key work regardless of modifier (Ctrl, Alt, etc) > key states, or to use the same key with different modifiers for different > bindings. How this worked before ("All"/"Normal") was pretty unclear, even in the > code comments. (New feature/bug fix.) If "Any" is not checked, the key binding should retain its present behavior, correct? That is, the key only works when no modifiers are being pressed. > * Removed possibility to rebind Ctrl, Shift, Alt, Meta modifiers. This feature > is very unusual and breaks the normal design pattern of using the designated > keys as keyboard modifiers. I'm not sure I understand the original problem. Could you please explain the rebinding part? Thanks, Kevin Zheng -------------- next part -------------- A non-text attachment was scrubbed... Name: rfc-2-updated.patch Type: text/x-patch Size: 57079 bytes Desc: not available URL: From arvidb at kth.se Wed Oct 30 20:06:13 2013 From: arvidb at kth.se (Arvid Brodin) Date: Thu, 31 Oct 2013 02:06:13 +0100 Subject: [crossfire] [RFC 2/3] Misc keybinding fixes and changes In-Reply-To: <5271978D.8000701@gmail.com> References: <526EF0F2.7000208@kth.se> <5271978D.8000701@gmail.com> Message-ID: <5271AD05.1070804@kth.se> On 2013-10-31 00:34, Kevin Zheng wrote: > Due to a minor change (addition of braces in a 'for' loop) in the > previous patch (RFC 1/3), the original patch no longer applies cleanly. > > I've attached a resolved patch that should apply correctly now. Please > tell me if something doesn't look right. Sorry for the confusion. I get this diff of keys.c after applying my patch #1 vs your patch #1: --- git-client/gtk-v2/src/keys.c 2013-10-31 01:57:39.109350292 +0100 +++ client-trunk/gtk-v2/src/keys.c 2013-10-31 01:51:03.551723009 +0100 @@ -186,11 +186,12 @@ static void insert_key(uint32 keysym, in * Try to find out if the command is a direction command. If so, keep * track of this fact, so in fire or run mode, things work correctly. */ - for (i = 0; i < 9; i++) + for (i = 0; i < 9; i++) { if (!strcmp(command, directions[i])) { direction = i; break; } + } newkey->keysym = keysym; newkey->flags = flags; @@ -763,7 +764,7 @@ static void parse_key(char key, uint32 k return; } if (key >= '0' && key <= '9') { - cpl.count = cpl.count*10 + (key - '0'); + cpl.count = cpl.count * 10 + (key - '0'); if (cpl.count > 100000) { cpl.count %= 100000; } It all looks good to me! Also, patch #3 applies with just some offset on top of the modified patches. > On 10/28/2013 18:19, Arvid Brodin wrote: >> * Add "Any" key modifier type; New "Any" button in keybindings dialog. This lets >> the user choose between having a key work regardless of modifier (Ctrl, Alt, etc) >> key states, or to use the same key with different modifiers for different >> bindings. How this worked before ("All"/"Normal") was pretty unclear, even in the >> code comments. (New feature/bug fix.) > > If "Any" is not checked, the key binding should retain its present > behavior, correct? That is, the key only works when no modifiers are > being pressed. Correct. >> * Removed possibility to rebind Ctrl, Shift, Alt, Meta modifiers. This feature >> is very unusual and breaks the normal design pattern of using the designated >> keys as keyboard modifiers. > > I'm not sure I understand the original problem. Could you please explain > the rebinding part? As a new player, I found this to be a usability problem. Everyone knows what the Control key is, but what's the Run key? I think the reason Shift and Ctrl were called Fire and Run is that it is possible to rebind them - i.e. if you want some other key to act as the Run modifier, you could 'bind runkey1 - and press a key to act as the Run modifier. (Actually I'm not even sure this worked; I never tried it - and I'm pretty sure it requires the run_on state to be kept in the server, since key repeats won't work if you bind Run to, say, 'r', and press that after the direction key... hmm. So it might be in the way of better handling of repeating keys.) I realise this change might not be liked by everyone, so feel free to shout out in that case (RFC = Request For Comments). But it did seem like a pretty unique and not very well advertised feature, so I chanced that it could be removed. > > Thanks, > Kevin Zheng > > > > _______________________________________________ > crossfire mailing list > crossfire at metalforge.org > http://mailman.metalforge.org/mailman/listinfo/crossfire -- Arvid From kevinz5000 at gmail.com Thu Oct 31 18:24:11 2013 From: kevinz5000 at gmail.com (Kevin Zheng) Date: Thu, 31 Oct 2013 18:24:11 -0500 Subject: [crossfire] [RFC 2/3] Misc keybinding fixes and changes In-Reply-To: <5271AD05.1070804@kth.se> References: <526EF0F2.7000208@kth.se> <5271978D.8000701@gmail.com> <5271AD05.1070804@kth.se> Message-ID: <5272E69B.9030000@gmail.com> Hi there, On 10/30/2013 20:06, Arvid Brodin wrote: > As a new player, I found this to be a usability problem. Everyone knows what > the Control key is, but what's the Run key? I think the reason Shift and Ctrl > were called Fire and Run is that it is possible to rebind them - i.e. if you > want some other key to act as the Run modifier, you could > > 'bind runkey1 I wasn't aware that this "feature" even existed. If it isn't present in the JXClient, I'd say it's safe to take it out. > - and press a key to act as the Run modifier. (Actually I'm not even sure this > worked; I never tried it - and I'm pretty sure it requires the run_on state to > be kept in the server, since key repeats won't work if you bind Run to, say, > 'r', and press that after the direction key... hmm. So it might be in the way > of better handling of repeating keys.) Probably not, repeating keys should be handled more elegantly. > I realize this change might not be liked by everyone, so feel free to shout out > in that case (RFC = Request For Comments). But it did seem like a pretty unique > and not very well advertised feature, so I chanced that it could be removed. You have my vote to take it out. Input from others would be helpful. Thanks, Kevin Zheng From davidnicholashurst at gmail.com Thu Oct 31 22:54:06 2013 From: davidnicholashurst at gmail.com (David Hurst) Date: Fri, 1 Nov 2013 14:54:06 +1100 Subject: [crossfire] [RFC 2/3] Misc keybinding fixes and changes In-Reply-To: <5272E69B.9030000@gmail.com> References: <526EF0F2.7000208@kth.se> <5271978D.8000701@gmail.com> <5271AD05.1070804@kth.se> <5272E69B.9030000@gmail.com> Message-ID: I'd be of the view, if it isn't reducing a functionality but re-implementing it in a more useful way (be it more flexible, faster, or any other good reason) then it has my support. Perhaps another perspective is that if you change it, and things don't work out, it is a lot easier to backtrack and try and different approach, than it is to have abandoned the idea before anyone got to play with it and never know what we were missing in the first place :). I would mention that at the moment windows users are really limited to the jxclient, so do try and make a change that can be implemented on both the gtk and jxclient. Saru On Fri, Nov 1, 2013 at 10:24 AM, Kevin Zheng wrote: > Hi there, > > On 10/30/2013 20:06, Arvid Brodin wrote: > > As a new player, I found this to be a usability problem. Everyone knows > what > > the Control key is, but what's the Run key? I think the reason Shift and > Ctrl > > were called Fire and Run is that it is possible to rebind them - i.e. if > you > > want some other key to act as the Run modifier, you could > > > > 'bind runkey1 > > I wasn't aware that this "feature" even existed. If it isn't present in > the JXClient, I'd say it's safe to take it out. > > > - and press a key to act as the Run modifier. (Actually I'm not even > sure this > > worked; I never tried it - and I'm pretty sure it requires the run_on > state to > > be kept in the server, since key repeats won't work if you bind Run to, > say, > > 'r', and press that after the direction key... hmm. So it might be in > the way > > of better handling of repeating keys.) > > Probably not, repeating keys should be handled more elegantly. > > > I realize this change might not be liked by everyone, so feel free to > shout out > > in that case (RFC = Request For Comments). But it did seem like a pretty > unique > > and not very well advertised feature, so I chanced that it could be > removed. > > You have my vote to take it out. Input from others would be helpful. > > Thanks, > Kevin Zheng > _______________________________________________ > crossfire mailing list > crossfire at metalforge.org > http://mailman.metalforge.org/mailman/listinfo/crossfire > -------------- next part -------------- An HTML attachment was scrubbed... URL: