WIMP Programming - Window Furniture
Steve Fryatt (216) 2105 posts |
That’s quite an antisocial way of doing it, though. At the very least, use Wimp_PollIdle with a suitable delay granularity so that the app isn’t hammering the task swapping whilst the delay is running. If Mike’s still using SFLib, then it’s actually fairly straightforward if the aim is to get a call back after mumble seconds’ delay. The Wimp_Poll loop needs to be a bit more complex so that SFLib can trigger Null Events on demand – here’s the relevant code from Launcher:
I suspect the main difference here from what Mike would have is the call to find the current time, the fiddling with the poll mask, and the use of With that in place, however, we can then just use SFLib’s
It can be seen in use here, in Launcher, for handling the mouse-over delay when opening panels. The There’s also a corresponding YMMV… Other Libraries Are Available… :-) |
Steve Fryatt (216) 2105 posts |
No, that’s not possible. Delays in the Wimp are a pain, because you can’t just loop until the time is up or the whole desktop will stop for the duration. This means that when you want a delay, you have to park what you’re doing neatly, enable Null events, call Wimp_Poll again, and then each time the Wimp returns to your application, check to see if the time is up and then go back to what you were doing. It’s a pain to do from scratch, which is why most Wimp libraries will have implemented some kind of callback system similar to the one that I described in SFLib. The SFLib code supports three scenarios, which are pretty much all that you’ll need:
I’m fairly sure that other libraries will be similar, but I’ll leave it to people who use them to explain. PS: Behind the scenes, SFLib maintains a queue of pending events and their times, so that it can ask Wimp_PollIdle to return only when it needs to. |
Mike Howard (479) 216 posts |
It may well be but it’s not clear how. I can see how changing the poll mask and setting the delay will work. Hang on. So, somewhere, wherever, in my code, I just Wimp_PollIdle to get my delay? This wimp_loop would be inside and independent of, the main loop. Senior phase over, hopefully. I’ll give that a go. Thanks for the nudges everybody. Edited, having just seen Steve Fryatt’s replys. I’ll read through those and yes, I’m still using SFLib. |
Steve Fryatt (216) 2105 posts |
No… no… no. That might work, but there’s a LOT of pain in store if you go down that route. Your inner loop will have to handle all of the events that your outer loop can handle, otherwise things quickly get… messy. And making sure that you’ve done that correctly will quickly get messy, too. The suggestion is to use Wimp_PollIdle in place of your current call to Wimp_Poll1 and then use some kind of state machine and timer arrangement to call the different steps of your code on Null Events after the desired delays have elapsed. Or use the tools offered by your chosen Wimp library, which will almost certainly have something up its sleeve. 1 The general rule of thumb is that, unless you know why you don’t want to do it, you always switch to Wimp_PollIdle if Null Events are not masked out. With Null Events enabled, Wimp_Poll will thrash the system’s task swapping, which aside from anything else, has bad effects on battery life for those using ARMBooks or emulators on other laptops. |
Dave Higton (1515) 3526 posts |
A GUI always requires event-driven programming. So the Wimp poll loop is the main loop, and all the other things that you do have to be consequences of events returned by the Wimp poll. If this is the first time you’ve done a GUI, it will probably be hard to change your thinking from linear programming. But it’ll soon become obvious. |
Mike Howard (479) 216 posts |
Thanks Steve, really useful. I’ll study the above and see what I can come up with. |
Mike Howard (479) 216 posts |
Ok. Noted.
Not the first time but my experience is limited. Thanks for the comments. |
Rick Murray (539) 13840 posts |
To put this into context, your app is basically going to be paged in, handed control, then paged out when you poll. Over and over and over. If the other apps are all being well behaved (PollIdle), it’ll pretty much just be you. Potentially ten thousand polls per second on a modern machine. I did write a program to record the Wimp’s polling speed, but it broke over 10K/sec. ;-) It was written circa 1994 for my A5000. Ten thousand pills was unimaginable. Use PollIdle. It’s the friendliest way. |
Dave Higton (1515) 3526 posts |
Please note that I only suggested that he poll idle for 1 second. But I agree, PollIdle is the proper way to do it. |
Mike Howard (479) 216 posts |
I’ve implemented a method using PollIdle and a callback event as suggested by Steve. Many thanks for that info Steve. It works perfectly. Onwards and upwards …. |
Steve Drain (222) 1620 posts |
Yes, that is the official way to do it, but the scheduling of multiple events can be tricky. SteveF has written a neat C library function to do it, but judging by my efforts to do the same in BASIC a good while back, it may not be so simple behind the scenes. I recall gerph once saying something along the lines of “any programmer can write schedules for PollIdle” and my reaction was “mmm…”. On the principle of minimum work for maximum return I noted that the OS has a perfectly good scheduling routine for its callback, but that this cannot be used by applications. So I wrote my simple Call module to make that possible. The SWIs Call_After/Call_Every register OS callbacks to a module routine that then sends a Wimp message to the task. Call_Remove unregisters the callback. That is it, more-or-less. I have used Call many times in the last decade or more and it is properly ‘allocated’. I think I am right, but Call has one tiny advantage over PollIdle in that Wimp messages have higher priority than other poll events, so the timing might just possibly be more precise. ;-) I await an avalanche of “you can’t do that!” responses. |
Rick Murray (539) 13840 posts |
You can’t do that! Well, you did ask. ;) I wonder if something changed regarding the Wimp timing between older releases (say 3.x) and current? Thankfully most of the timing I’ve needed to do has been in the order of a half second or so, which meant the simplest approach was to claim null events and Wimp_Poll while checking MetroGnome each time around. I’m sure there was a reason why I didn’t PollIdle, but I can’t remember it now. However, if you’ve needed to create a module to help with this, maybe there’s an actual deficiency that ought to be addressed? |
Mike Howard (479) 216 posts |
Well, done more plodding and I have another question. Not sure whether I should start a new thread or keep my questions here in this thread. I feel I shouldn’t be stuck on this but I am! Dragging tiles is tripping me up, I can’t figure how to get the sprite name associated with the tile. It’s probably dead obvious but once I have a block I get tunnel vision. Using a constant, say “T” for the sprite name, to test, with
works as I would expect. To get the real name I thought I would use wimp_icon_state, specifically, indirected_sprite.id, so
debug_printf(“sprite name is %s\n”, nid); Which prints the single character sprite name but I can’t figure out how to use it in ‘dragasprite_start’. I need a ‘char constant’ but I have an ‘osspriteop_id’, which according to OSLib is ‘typdef struct {…} *osspriteop_id’ … but should be a union …. Confused. |
Steve Fryatt (216) 2105 posts |
RISC OS can use either IDs or names to refer to sprites, and OSLib struggles a bit with this. So long as you know that you’re using names, you should just be able to do
to use it as a pointer to a name. It’s a bit ugly, but is one of the few places where this seems to happen in OSLib. I wrote about it under ‘Indirected sprite icons’ in my tutorial, if it’s any help, although re-reading it, it could be a lot clearer. That’s why I need an editor. |
Mike Howard (479) 216 posts |
Well cast to char was the what I first thought would be the case and then seeing the result of the debug_printf just strengthened this thought. I just couldn’t get it to work. Obviously I was too tired by this point and needed to go to bed. I have got it working now and I had re-read the info on your tutorial which I remember sparking something but I must have got sidetracked. I knew I would sort it as soon as I asked for help :-) |
Mike Howard (479) 216 posts |
So, summer is over and I’m back at my desk and moving on with my scrabble clone. I’m currently looking at how to indicate who’s turn it is to play, as it’s possible/likely multiple rack windows will be on the desktop at the same time. One of my first thoughts was to differentiate (on each turn) between racks by changing the background colour of the title bar of the player’s rack window but I can’t see that it’s possible, once a window has been created. I could delete it and recreate it I suppose but that seems like a lot of work for what I expected to be a simple task. Next I thought I could just programmatically ‘select’ the window in question (in a similar way to icons), thus using the built in capability of the WIMP to to display it’s ‘clicked’ state, but I can’t see that that is possible either. Is there anyway to achieve either of the above? There are obviously other options but the above seem intuitive to me. |
Paolo Fabio Zaino (28) 1882 posts |
Why not just change the Window Title? Something like “Game name – Player 1 turn” to “Game name – Player 2 turn”. Dealing with colours is always tricky. What if a user has a Theme with a colourset that will make it look ugly or text disapear? What about colour blind people? Just my 0.5c
If you mean to give caret to the window, that may be also tricky, what if a user clicks on an editor window by mistake and the caret is moved away before they make their move? In general I’d avoid any window action that is not “local” to your App. In other words, actions that could be overriden by other Apps (like bringing a window to foreground or give it caret/focus). Again, just my 0.5c Hope this helps |
Steve Fryatt (216) 2105 posts |
That’s pretty much how it works, I’m afraid: you can mess with a window’s visible area, work area, stack position and flags after it has been created, but I think that’s it. That said, the idea is a bad one from a Style Guide perspective, too…
Windows don’t have a “clicked” state…? If you’re referring to the yellow-highlighted title bar, then that’s input focus: you set it using Wimp_SetCaretPosition. However, remember that the user can move the caret anywhere they wish and so unset it again. Oh, and moving the caret without the user clicking somewhere to place it is a bad idea. Consider the possibility that someone is playing your game whilst working on a document: suddenly, mid-word, all of their typing diverts into one of your tile windows…
Both feel counter to the Style Guide to me: the colour of the window furniture is the preserve of the user’s selected Theme (or it certainly used to be), and you should really leave windows using the colours defined in the Style Guide for this reason. How the caret can move around the desktop is also well defined. A better option would be to put the highlight inside your window. A coloured border around the edge of the work area would be one option, or highlights around the tiles, or something else along those lines? Visibly shading the whole tile-rack for those players whose turn it isn’t (perhaps monochroming them, or similar) might be another idea? |
Mike Howard (479) 216 posts |
Good points.
Again, good points.
Yeah, I agree, but sticking rigidly to the Style Guide can’t always be right :-) I did think my first two options were no go.
Ok, this does seem the best option. Highlights (borders) is I think the way to go, along with changing the title text as suggested by Paolo. Thanks for the comments. |
Rick Murray (539) 13840 posts |
Simply changing the title text ought to be sufficient. As you can see, I have reserved highlighting for cues for matching cards. To much stuff changing can be jarring and counterintuitive. Yellow border – previous cards played this turn (you play a card from your hand, then the next one from the stack). (the game is Koi-Koi and it is on Store) |
Mike Howard (479) 216 posts |
That’s my first task. Changing the title text seems straight forward but updating the display not so much. OSLib has ‘wimp_force_redraw_title’ but it’s 3.8+. What’s the process on earlier os versions, for windows that are already open? |
Paolo Fabio Zaino (28) 1882 posts |
I have a feeling that you may have mis-interpreted OSLib: SWI Wimp_ForceRedrawTitle = ( NUMBER 0x400D1 "Forces redraw of title for a window - nested Wimp / RISC OS 3.8+", ENTRY ( R0 = Wimp_W: w, R1 # 'TASK', R2 # Wimp_FurnitureTitle //is 3 // R3 ... DON'T NEED // R4 ... DON'T NEED ), EXIT ( R0?, R1? //Ursula clears R1; R2-R4 preserved ) ); That, to me, means either Nested Wimp OR from RO 3.8+. In other words just add this to your !Run file to be safe: RMEnsure WindowsManager 3.98 Error This App requires WindowsManager 3.98 or higher Eventually you could create your own way to update a Window’s Title, by using Wimp_GetWindowOutline (to get the entire size of the window, including title bar and funrinture) and then pass it to Wimp_ForceRedraw. This should work even on super-old RISC OS (may work on RO 2 also, but my memory is rusty, so not sure). Also, just as a recommendation, if you are writing software that should run also on older releases of RO and older machines, make sure you have those machines to test on, don’t assume that an API is going to solve issues and make your software automagically work everywhere (especially when compiled). Hope this helps |
Mike Howard (479) 216 posts |
Ok, many thanks.
Yes, I was thinking this was the way to go. Currently though I’ve run into a problem just writing to the title bar. Unless I use a string constant such as “Mike – To Play” or such, which is obviously of no use, I’m getting malloc heap crashes. I don’t yet understand why.
I do, and will. Due to the way I have been learning, the current attempt is though not usable on 3.1 as I’m using DrawFile Render. I intend to do a complete teardown once this initial attempt is complete.
It very much does, thank you. |
Steve Fryatt (216) 2105 posts |
They’re synonymous, because to enable the Nested Wimp functionality you pass 380 to Wimp_Initialise:
(see my notes on the Nested Wimp for a very broad-brush outline on this).
It’s worth noting, though, that for most applications, the Nested Wimp is pretty much a minimum requirement now. It’s 20+ years old, and even if you don’t need anything that it offers, many applications will need things like Wimp_ResizeIcon which is 3.5+. At which point you get on to the question of how many RiscPCs are out there and receiving new software whilst still being on RISC OS 3.5/6/7. There are other factors, of course — and if you’re aiming for the “retro” market then 3.1 compatibility is a valid thing to want. Just don’t get hung up on it if all that you’re trying to do is to write a useful application for “normal users”: the Nested Wimp has a lot of useful stuff in it that isn’t to do with being “nested”. And for many modern UI designs, the nested bit is invaluable, too.
Are the titles indirected? And make sure that the indirected buffers are long enough for what you’re trying to write (including the terminator). I’m not aware of any template editors screwing this up for text icons1, but it may be useful to create the templates with titles that are the maximum length that you want the buffer to be, to force the editor to allocate enough space. 1 Sprite ones, on the other hand… |
Rick Murray (539) 13840 posts |
No, they aren’t. That they are similar doesn’t help, but the nested Wimp has been released for older machines, which still needs the 380 magic number, but it’s the expected Wimp version.
Any RiscPC emulation running in a non-Select 26 bit world? |