05 July 2021, 15:57 | #1 |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
Converting to Reaction: Shared UserPort?
I have an app designed without Reaction, and now that 3.2 is available, I'm trying to convert it to work with Reaction. One of the things that is different is event handling. I may describe this wrong, but this is how I think the differences come out:
My app uses windows like this: - a backdrop window, opened on launch - user opens 1 to n additional windows as they want to. Its basically a file manager, so if you double click on a file icon for a folder, another window opens showing contents of that folder. - you can do an About menu item and an About Window opens. - other window types to come (config, etc.) I added the About Window after reaction came out, so it has been my testbed. There will only ever be 1 about window open, so it's pretty straight forward. But the main window type, there could be any number of them. I am using a shared userPort for all of them. When a signal comes in: - is it for the about window? let it process it. STOP Is it possible to use a shared signal with Reaction windows? What's the best design for a case like this? - Do GetAttr for each window as it is made, and store that in the parent window class. add all those signals together in the signal mask, then iterate through to find out which one it was? - something else entirely? Current event handler: Code:
// wait for events for the currently active window. // having it centralized makes it possible to have user close first window gracefully // requires that WBSurfaces keep track of which one is active, which isn't hard. void WaitForEvents(void) { WindowRequiredAction action_needed = action_none; struct IntuiMessage* the_message; struct Window* window_with_message; ULONG window_event_signal = 1L << global_app->shared_user_port_->mp_SigBit; ULONG launched_app_signal = 1L << global_app->launched_app_reply_port_->mp_SigBit; ULONG our_idcmp_signals = window_event_signal | launched_app_signal; ULONG about_window_signal; ULONG signals_we_want; ULONG signals_received; // LOGIC: // we will listen at 2 ports: // - one for window events. this is the user user port that we set for all WB2KSurfaces // - one that we set up to be a port where apps we launch can reply back to when they are quit // we combine all signals (2 in this case), then use Wait() instead of WaitPort(). // we also need to listen on a per-window basis for Reaction signals, as reaction does not seem to work with shared ports // at current time (6/30/2021), only About window uses reaction, so we can check for that window specifically // need a more dynamic solution when we move the main window type to reaction (iterating through all app's surfaces is probably slow) while (action_needed != action_quit_app) { signals_we_want = our_idcmp_signals; if (global_app->about_window_ != NULL) { GetAttr(WINDOW_SigMask, global_app->about_window_->objects_[ABOUT_OID_MAIN], &about_window_signal); signals_we_want |= about_window_signal; } signals_received = Wait(signals_we_want); if (signals_received & about_window_signal) { //DEBUG_OUT(("WaitForEvents %d: about win reaction signal detected!", __LINE__)); action_needed = AboutWindow_HandleEvents(global_app->about_window_); if (action_needed == action_close_window) { App_CloseAboutWindow(global_app); } } else if (signals_received & launched_app_signal) { //DEBUG_OUT(("WaitForEvents %d: launched_app_signal received", __LINE__)); App_HandleLaunchedAppSignal(); } else if (signals_received & window_event_signal) { //DEBUG_OUT(("WaitForEvents %d: window_event_signal received", __LINE__)); while ((the_message = (struct IntuiMessage*)GetMsg(global_app->shared_user_port_)) != NULL) { // determine which window this event was directed at // acquire the message, because it has a pointer to the window struct //the_message = (struct IntuiMessage*)GetMsg(global_app->shared_user_port_); window_with_message = the_message->IDCMPWindow; //DEBUG_OUT(("WaitForEvents %d: window_with_message=%p", __LINE__, window_with_message)); WB2KList* the_item = *(global_app->list_surfaces_); if ( the_item == NULL ) { LOG_WARN(("WaitForEvents %d: no windows available??", __LINE__)); } else { boolean signal_not_matched = true; while (the_item != NULL) { WB2KWindow* this_surface = (WB2KWindow*)(the_item->payload_); //DEBUG_OUT(("WaitForEvents %d: window_with_message=%p; this_surface=%p", __LINE__, window_with_message, this_surface->window_)); // is this the item we are looking for? if (this_surface->window_ == window_with_message) { //printf("WaitForEvents: event detected for surface %p, window %p \n", this_surface, this_surface->window_); signal_not_matched = false; this_surface->intuimsg_ = the_message; action_needed = Window_HandleEvents(this_surface); if (action_needed == action_close_window) { App_CloseOneWindow(global_app, the_item); } break; } the_item = the_item->next_item_; } if (signal_not_matched) { LOG_ERR(("WaitForEvents %d: a message couldn't be matched to a window", __LINE__)); App_Destroy(); // crash early, crash often } } } } } } |
05 July 2021, 22:14 | #2 | ||
Registered User
Join Date: Jan 2002
Location: Germany
Posts: 7,038
|
Quote:
Quote:
|
||
05 July 2021, 22:50 | #3 | |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
Quote:
A500 w/ 1.3 -> A3000 w/ 2.04 ->>>>>MANY DEPRESSING EVENTS >>>>> me doing non-Amiga stuff -->>>> 2018 or so: FS-UAE with WB 3.1 >>>> A2000 w/ 3.1.4 >>> A3000 w/3.2 So I've never had or even seen 3.9. Let alone any PPC 4.x type system. I did try to get ClassAct working a year or two ago while waiting for 3.2, but didn't have much luck (and there were lots of other low-level stuff to work on in the meantime). I've got an idea to try out here to figure out which window is getting the event, event without a message->window arrangement to take advantage of. |
|
06 July 2021, 15:28 | #4 | |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
This was helpful, thanks. I was still leaving it out, and then adjusting it after creating the window object (which, under non-reaction, apparently saves Intuition from creating an extra user port when window is created). I switched to specifying the user port right in the WindowObject creation call, and that gets it set.
I admit, however, that I don't understand WHY this works. From window_cl.doc: Quote:
I took the plunge yesterday and started the conversion. Long way to go, but it's coming along. |
|
06 July 2021, 16:34 | #5 |
Registered User
Join Date: Jan 2002
Location: Germany
Posts: 7,038
|
Each window.class object knows which Intuition window it opened and it handles messages only from that window.
When working with objects, you should never poke into the returned memory. Always work with SetAttrs or DoMethod. Especially the struct Window pointer you get from WM_OPEN belongs to the class. You must not do anything with it except to forward it unchanged to RefreshGList and other such functions which require a window pointer. |
08 July 2021, 15:26 | #6 |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
I will do a little audit of what I'm doing to this window. It's been a year or so since I really worked on it, I don't remember everything I'm doing.
I did make a lot of progress on the reaction conversion. Most things are working again. A couple of things I'm stuck with though: 1. how do I get the scrollbars back into the window borders? I have taken out all the old code I had that positioned the scrollbars in the borders using GA_RelBottom, etc. My assumption is that when I make them part of the reaction layout, I shouldn't be doing that sort of thing. 2. I can't pick up events for the scrollbars. I have an IDCMP hook set up: the scroller objects: Code:
// create the scrollbar objects and the string object used for renaming files the_surface->gadgets_[MAIN_GID_H_SCROLL] = ScrollerObject, GA_ID, MAIN_GID_H_SCROLL, SCROLLER_Orientation, SORIENT_HORIZ, GA_Immediate, TRUE, GA_RelVerify, TRUE, ICA_TARGET, ICTARGET_IDCMP, ScrollerEnd; the_surface->gadgets_[MAIN_GID_V_SCROLL] = ScrollerObject, GA_ID, MAIN_GID_V_SCROLL, SCROLLER_Orientation, SORIENT_VERT, GA_Immediate, TRUE, GA_RelVerify, TRUE, ICA_TARGET, ICTARGET_IDCMP, ScrollerEnd; Code:
WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE | IDCMP_VANILLAKEY | IDCMP_RAWKEY | IDCMP_GADGETUP | IDCMP_IDCMPUPDATE | IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW | IDCMP_CHANGEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_MENUPICK | IDCMP_EXTENDEDMOUSE, WINDOW_IDCMPHook, &the_surface->scroller_hook_, WINDOW_IDCMPHookBits, IDCMP_IDCMPUPDATE | IDCMP_EXTENDEDMOUSE, WINDOW_AppPort, global_app->launched_app_reply_port_, WINDOW_SharedPort, the_user_port, Code:
// set up an IDCMP hook that will let us examine IDCMP messages to see if scrollers have been moved the_surface->scroller_hook_.h_Entry = (void *)Window_ScrollerHook; the_surface->scroller_hook_.h_SubEntry = NULL; the_surface->scroller_hook_.h_Data = (APTR)the_surface; Code:
// hook function to receive IDCMP events, and check them for scrollbar movement // based on code found here: https://github.com/pcwalton/NetSurf/blob/master/netsurf/amiga/tree.c\ami_tree_scroller_hook void Window_ScrollerHook(struct Hook* hook, Object* object, struct IntuiMessage* msg) { ULONG activated_gadget_id; ULONG x; ULONG y; WB2KWindow* the_surface = (WB2KWindow*)hook->h_Data; struct IntuiWheelData* wheel; DEBUG_OUT(("Window_ScrollerHook %d: msg->Class = %lu, code=%u, qualifier=%u, addr=%p", __LINE__, msg->Class, msg->Code, msg->Qualifier, msg->IAddress)); switch(msg->Class) { case IDCMP_IDCMPUPDATE: activated_gadget_id = GetTagData( GA_ID, 0, msg->IAddress ); DEBUG_OUT(("Window_ScrollerHook %d: activated_gadget_id = %lu", __LINE__, activated_gadget_id)); if (activated_gadget_id == MAIN_GID_H_SCROLL || activated_gadget_id == MAIN_GID_V_SCROLL) { signed long new_pos; new_pos = Gadget_GetTop(the_surface->gadgets_[activated_gadget_id]); DEBUG_OUT(("Window_ScrollerHook %d: scrollbar gadget %lu = %li", __LINE__, activated_gadget_id, new_pos)); if (activated_gadget_id == MAIN_GID_H_SCROLL) { the_surface->content_left_ = new_pos; } // draw content without recalculating icon positions Panel_RegenerateDisplay(the_surface->active_panel_); } break; case IDCMP_EXTENDEDMOUSE: // TODO: mousewheel support! // if(msg->Code == IMSGCODE_INTUIWHEELDATA) // { // wheel = (struct IntuiWheelData *)msg->IAddress; // // ami_tree_scroll(twin, (wheel->WheelX * 20), (wheel->WheelY * 20)); // } break; } } |
08 July 2021, 18:06 | #7 |
Registered User
Join Date: Jan 2002
Location: Germany
Posts: 7,038
|
The h_Entry entry point of a hook receives arguments in registers (A0, A2, A1). Your hook function however seems to expect arguments on the stack.
To continue doing so you should initialize h_Entry with HookEntry and set h_SubEntry to your hook function. HookEntry is part of amiga.lib and declared in clib/alib_protos.h. |
09 July 2021, 00:52 | #8 | |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
Quote:
I wish there was a compilation of programs that came with source code. I find source code to be the easiest thing to learn from, but it can be hard to find relevant examples for the Amiga. More info and examples in Autodocs would help a lot of course. |
|
09 July 2021, 15:29 | #9 | |
Registered User
Join Date: Oct 2012
Location: Germany
Posts: 585
|
Quote:
|
|
09 July 2021, 16:52 | #10 |
Coder/webmaster/gamer
Join Date: Oct 2001
Location: Canberra/Australia
Posts: 2,698
|
There are a lot of ReAction examples in the 3.2 NDK; also you could look at the source code of programs like MCE or Report+.
|
10 July 2021, 07:23 | #11 | |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
Quote:
Thanks for the suggestions of MCE and Report+. I have downloaded both, and added to my source code references folder. MCE is a bit of a beast! That's a real labor of love. I downloaded a couple of games just so I could see what the UI looked like. I also search a bit through aminet from time to time, but you can't really search for "reaction" and "download includes source code". Were there ever any FTP sites for Amiga that had dedicated source folders? I'm thinking of, for old Mac, there was UMich FTP which had a folder of source files. That's been very helpful. I should probably spend more quality time with FredFish disks. |
|
10 July 2021, 07:29 | #12 |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
I've noticed that trying to document something takes approximately the same amount of time as coding and researching it. And documentation is hard brain work (to do well). But yeah, I hear you! I need to get further along here, but definitely thinking about how to share back some info.
|
10 July 2021, 07:38 | #13 |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
About the scrollbars. I reached the conclusion that it wasn't possible to put them into the window borders using pure Reaction. Am I right about that?
These are my personal dev notes on it: ------ Reaction Windows and Scrollbars * This was very confusing * I started out by doing standard reaction layout: added scrollbars to a layout. They auto refresh, etc. Figured out how to set top for them: slightly different than normal gadgets, you don’t use PGA_Top, you use SCROLLER_Top, etc. * The problem with this method is that A) the scrollbars never get into the window frame. You can use PG_BorderBottom, but that just colors the scroller blue (frame color), it doesn’t move it. So you end up using up valuable screen real estate inside your window. * I did figure out how to remove and add them back in when needed/not needed, under Reaction. It was a bit klutzy looking. * In the end, I figured out that the old method not only worked best, it was doable even under reaction. I ended up deciding I don’t ever need the V scroller, so I just don’t draw that. For the H scroller, it goes into the bottom border, and only renders when needed. * The only downside I noticed is that the scrollbars are not in fact reaction, so the reaction UI preferences don’t modify them. Don’t think that’s a real problem. In this UI, there's a pretty basic Reaction layout with a speedbar at the top, then a spacer where I blit the icons and labels onto. The icons don't exist as gadgets at all. The scrollbar at the bottom is a gadget, but not a reaction gadget. I literally went back into GitHub and got out my old (pre-reaction) scrollbar code and dusted it off, and it worked in the border. Code:
// Recalculate the scrollbar sliders and reset scrollbar position to top/left // call this when required size of the window is different than what it had been void Window_ResetScrollbars(WB2KWindow* the_surface) { //DEBUG_OUT(("Window_ResetScrollbars %d: content left=%i, avail=%li, needed=%li", __LINE__, the_surface->content_left_, the_surface->inner_width_, the_surface->required_inner_width_)); // hide or show the scrollbars, depending on need if (the_surface->inner_width_ < the_surface->required_inner_width_) { // if scrollbar not already visible, add it to the layout. if visible, adjust it's current/max values if (the_surface->h_scroller_visible_ == true) { Gadget_SetTop(the_surface->gadgets_[MAIN_GID_H_SCROLL], the_surface, the_surface->required_inner_width_, the_surface->inner_width_, the_surface->content_left_); } else { // LOGIC: // Create a new scrollbar object, and then use AddGList to attach it to the end of the list of gadgets // it is necessary to call RefreshGadgets() here, but not necessary to explicitly refresh the window frame. // we need to dynamically get the width/height of the resize gadget in case it has changed struct Screen* scr = global_app->screen_; // NOTE: these MUST be "ULONG" as of 3.2, based on how VBCC defines unsigned long // See: https://eab.abime.net/showthread.php?p=1490903#post1490903 ULONG win_resize_gadget_width = 18; ULONG win_resize_gadget_height = 11; // Default size (pre 3.2?) struct Image* win_resize_image; // figure out how big the Window sizing gadget image is, so we can have scrollbars not collide with it if ( (win_resize_image = NewObject (NULL, (STRPTR)SYSICLASS, SYSIA_Which, SIZEIMAGE, SYSIA_DrawInfo, the_surface->draw_info_, TAG_DONE)) != NULL) { // Get size gadget geometry GetAttr (IA_Width, (APTR)win_resize_image, &win_resize_gadget_width); GetAttr (IA_Height, (APTR)win_resize_image, &win_resize_gadget_height); // don't need the image object any more DisposeObject (win_resize_image); } if ( (the_surface->gadgets_[MAIN_GID_H_SCROLL] = NewObject (NULL, (STRPTR)PROPGCLASS, GA_ID, MAIN_GID_H_SCROLL, GA_RelBottom, - win_resize_gadget_height + ((win_resize_gadget_height > 15) ? 4 : 3), GA_Left, scr->WBorLeft, GA_Height, win_resize_gadget_height - ((win_resize_gadget_height > 15) ? 6 : 4), GA_RelWidth, - (win_resize_gadget_width + scr->WBorLeft + 3), GA_BottomBorder, TRUE, GA_DrawInfo, the_surface->draw_info_, PGA_Freedom, FREEHORIZ, PGA_Borderless, ((the_surface->draw_info_->dri_Flags & DRIF_NEWLOOK) && (the_surface->draw_info_->dri_Depth != 1)), PGA_NewLook, TRUE, PGA_Total, the_surface->required_inner_width_, PGA_Visible, the_surface->inner_width_, PGA_Top, the_surface->content_left_, ICA_TARGET, ICTARGET_IDCMP, TAG_DONE)) == NULL) { LOG_ERR(("Window_ResetScrollbars %d: couldn't create H scroller object", __LINE__)); App_Destroy(); // crash early, crash often } AddGList(the_surface->window_, (struct Gadget *)the_surface->gadgets_[MAIN_GID_H_SCROLL], (UWORD)~0, 1, NULL); RefreshGadgets((APTR)the_surface->gadgets_[MAIN_GID_H_SCROLL], the_surface->window_, NULL); the_surface->h_scroller_visible_ = true; DEBUG_OUT(("Window_ResetScrollbars %d: added H scroller", __LINE__)); } } else { // if scrollbar is visible, remove it from the layout if (the_surface->h_scroller_visible_ == true) { // LOGIC: // Use RemoveGList to remove the scroll bar, then refresh the window frame explicitly // If window frame isn't refreshed on removal, the scroll bar visuals will stay until next time window is resized or made inactive/active. // When adding the scrollbar, refreshing the window frame is not necessary. Calling RefreshGadgets() here does not help. RemoveGList( the_surface->window_, (struct Gadget *)the_surface->gadgets_[MAIN_GID_H_SCROLL], 1L ); RefreshWindowFrame(the_surface->window_); the_surface->gadgets_[MAIN_GID_H_SCROLL] = NULL; the_surface->h_scroller_visible_ = false; } } } |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
ClassAct/Reaction & Eclipse/GCC Cross Chain | Warty | Coders. C/C++ | 3 | 15 August 2020 18:41 |
installing Classact and Reaction on 3.1.4 | dschallock | support.Other | 8 | 06 February 2020 19:19 |
Shared configuration | Anubis | support.FS-UAE | 2 | 25 April 2016 23:51 |
Update ReAction classes | mritter0 | Coders. General | 2 | 30 July 2014 01:55 |
Proper ReAction coding | mritter0 | Coders. C/C++ | 3 | 24 April 2014 07:34 |
|
|