Child window message loop

Just starting out? Need help? Post your questions and find answers here.
User avatar
the.weavster
Addict
Addict
Posts: 1577
Joined: Thu Jul 03, 2003 6:53 pm
Location: England

Re: Child window message loop

Post by the.weavster »

Blue wrote:
TI-994A wrote:[...]
Nevertheless, it should be noted that even windows from other processes would be able to dismiss this child window if they were to automatically pop-up or receive focus.
Oops ! That’s an unexpected curve. But now that you point it out, it does seem obvious.
I guess you could move the event handling from #PB_Event_DeactivateWindow to #PB_Event_ActivateWindow so when a window is activated it closes any other open windows you want rid of. You'll just have to cycle over the windows you want to display that behaviour, test each one with IsWindow() and if so close it.
User avatar
TI-994A
Addict
Addict
Posts: 2740
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Child window message loop

Post by TI-994A »

Blue wrote:...try the latest updated code provided by the.weavster, but with the 2 modified procedures I submitted. Can you think of an independent process that would interfere unexpectedly with that child window ?
I've added a small routine that would trigger-launch NotePad five seconds after the child window is opened. Although there are no clicks whatsoever, the child window would be closed once NotePad launches and receives focus.

Code: Select all

Enumeration windows
  #frmMain
  #frmChild
EndEnumeration

Enumeration gadgets
  #frmMain_btnChild 
  #frmChild_btnOK
  #frmMain_txtClock
  #frmChild_txtClock
EndEnumeration

Enumeration timers
  #frmMain_tmrClock
  #frmChild_tmrClock
EndEnumeration

;; modified by Blue
Procedure frmChild_Open()
  If OpenWindow(#frmChild,#PB_Ignore,#PB_Ignore,250,150,"child",#PB_Window_SystemMenu)
    ButtonGadget(#frmChild_btnOK,10,10,150,30,"Message Box")                    ; <<< changed
    TextGadget(#frmChild_txtClock,20, 110, 210, 30,
               FormatDate("%hh:%ii", Date()), #PB_Text_Right)
    AddWindowTimer(#frmChild, #frmChild_tmrClock, 1000)
    ;#############################
    SetWindowData(#frmChild,#True) ; deactivating the window will close it      ; <<< changed
    ResizeWindow(#frmChild,WindowX(#frmMain)+80,WindowY(#frmMain)+80,#PB_Ignore,#PB_Ignore) ;so we can always see it
    SetWindowTitle(#frmChild,"Closes automatically")                            ; <<< changed
    ;#############################
    
    Global t = ElapsedMilliseconds()
    
  EndIf
EndProcedure

Procedure frmChild_onGadget(nGadget,nEventType,nX,nY)
  Select nGadget
    Case #frmChild_btnOK
      ;##############################
      SetWindowData(#frmChild,#False)
      SetWindowTitle(#frmChild,"Remains active")
      nResponse = MessageRequester("Message","something important... or not !",#PB_MessageRequester_Ok)
      SetWindowData(#frmChild,#True)
      SetWindowTitle(#frmChild,"Closes automatically")                          ; <<< changed
      ;##############################      
  EndSelect
EndProcedure

; app's main window
Procedure frmMain_Open()
  If OpenWindow(#frmMain,#PB_Ignore,#PB_Ignore,400,300,"parent")
    ButtonGadget(#frmMain_btnChild,10,10,150,30,"Show Child")
    TextGadget(#frmMain_txtClock,280, 20, 100, 30,
               FormatDate("%hh:%ii:%ss", Date()), #PB_Text_Right)
    AddWindowTimer(#frmMain, #frmMain_tmrClock, 1000)
  EndIf 
EndProcedure

Procedure frmMain_onGadget(nGadget,nEventType,nX,nY)
  Select nGadget
    Case #frmMain_btnChild
      frmChild_Open()
  EndSelect
EndProcedure

Procedure Events_Timer()
  Static minuteUpdatedTime.s, c
  nTimer = EventTimer()
  Select nTimer
    Case #frmMain_tmrClock
      ;updates every second
      SetGadgetText(#frmMain_txtClock,
                    FormatDate("%hh:%ii:%ss", Date()))
    Case #frmChild_tmrClock
      ;updates every minute
      
      If ElapsedMilliseconds() - t > 1000
        SetGadgetText(#frmChild_txtClock, "I'm going bye-bye in " + Str(5 - c) + "...")
        t = 0 : c + 1
        If c > 5
          c = 0
          RunProgram("notepad.exe")  
        EndIf
      EndIf
            
      currentTime.s = FormatDate("%hh:%ii", Date())
      If minuteUpdatedTime <> currentTime
        minuteUpdatedTime = currentTime
        SetGadgetText(#frmChild_txtClock, currentTime)
      EndIf     
  EndSelect 
EndProcedure

Procedure Events_Gadget()
  nGadget = EventGadget()
  nEventType = EventType()
  nX = 0 : nY = 0
  Select GadgetType(nGadget)
    Case #PB_GadgetType_Canvas
      nX = GetGadgetAttribute(nGadget,#PB_Canvas_MouseX)
      nY = GetGadgetAttribute(nGadget,#PB_Canvas_MouseY)
    Case #PB_GadgetType_ListIcon
      nY = GetGadgetState(nGadget)
      ; etc...
  EndSelect
  Select EventWindow()
    Case #frmMain
      frmMain_onGadget(nGadget,nEventType,nX,nY)
    Case #frmChild
      frmChild_onGadget(nGadget,nEventType,nX,nY)
  EndSelect
EndProcedure

;################################
;Split the handlers
Procedure Events_CloseWindow()
  nWin = EventWindow()
  Select nWin     
    Case #frmMain
        End ; closing main window quits app
    Default
      CloseWindow(nWin)
  EndSelect 
EndProcedure

Procedure Events_DeactivateWindow()
  nWin = EventWindow()
  Select nWin     
    Case #frmChild ; list all windows you want this behaviour for
      If GetWindowData(nWin) = #True
        CloseWindow(nWin)
      EndIf
  EndSelect   
EndProcedure
;###############################

BindEvent(#PB_Event_Gadget,@Events_Gadget())
BindEvent(#PB_Event_Timer,@Events_Timer())
BindEvent(#PB_Event_CloseWindow,@Events_CloseWindow())
;###############################
;point this to the new handler
BindEvent(#PB_Event_DeactivateWindow,@Events_DeactivateWindow())
;###############################

frmMain_Open()
Repeat
  nWait = WaitWindowEvent()
ForEver
I believe that there are no native contingencies for that.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
TI-994A
Addict
Addict
Posts: 2740
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Child window message loop

Post by TI-994A »

the.weavster wrote:
Fred wrote:When starting with PB, it's probably a very good advice to use only one event loop.
To my mind there is only one event loop in my code. Taking gadget events as an example, Events_Gadget() is the only callback bound to gadget events and that simply routes to other procedures based on the associated window id, gadget id and event type. Would you agree with that, Fred?
I don't believe that he's referring to your example. IMHO, the object-based callbacks are very clean and structured. It's a good model. :)
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
TI-994A
Addict
Addict
Posts: 2740
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Child window message loop

Post by TI-994A »

User_Russian wrote:...If you call BindEvent several times with identical parameters, a memory leak occurs.

Code: Select all

Procedure Child()
  If OpenWindow(1, #PB_Ignore, #PB_Ignore, 200, 150, "child", #PB_Window_SystemMenu, WindowID(0))
    BindEvent(#PB_Event_CloseWindow, @Child_Event(), 1)
    For i=1 To 1000000
      BindEvent(#PB_Event_CloseWindow, @Child_Event(), 1)
    Next i
  EndIf
EndProcedure
You're right; neither the UnbindEvent() nor the CloseWindow() functions seem to release the memory captured by the BindEvent() function. The memory footprint simply keeps growing with every call.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
Blue
Addict
Addict
Posts: 966
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Child window message loop

Post by Blue »

TI-994A wrote:...
I've added a small routine that would trigger-launch NotePad five seconds after the child window is opened. Although there are no clicks whatsoever, the child window would be closed once NotePad launches and receives focus.
...
Clever demonstration, TI-994A. Much appreciated.


@ the.weavster
As TI-994A pointed out, your way of coding is very good. It uses a single events loops, which is obvious just from reading the code.
Fred's comment could only be directed at my admission that I keep using dedicated events loops for my child windows.
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
User avatar
Blue
Addict
Addict
Posts: 966
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Child window message loop

Post by Blue »

TI-994A wrote:
User_Russian wrote:...If you call BindEvent several times with identical parameters, a memory leak occurs.
[...]
You're right; neither the UnbindEvent() nor the CloseWindow() functions seem to release the memory captured by the BindEvent() function. The memory footprint simply keeps growing with every call.
@ User_Russian :
In light of TI-994A's confirmation of your observation concerning the memory leak, you really should post that as a bug, regardless of what Fred had to say about it.
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
DontTalkToMe
Enthusiast
Enthusiast
Posts: 334
Joined: Mon Feb 04, 2013 5:28 pm

Re: Child window message loop

Post by DontTalkToMe »

Blue wrote: In light of TI-994A's confirmation of your observation concerning the memory leak, you really should post that as a bug, regardless of what Fred had to say about it.
I don't think there is a memory leak, or at least I don't think what shown in the task manager can be used as supporting evidence.
When you free resources, this is not necessary reflected in the task manager in real time.
Some pages of memory results as allocated in the task manager until the memory manager trims pages from the working set to create more available memory. It does that when it thinks it's the right moment to do so.
If you manually force the trim of those pages using the appropriate API call, the task manager reflects that immediately and there is no longer an apparent memory leak.
User avatar
TI-994A
Addict
Addict
Posts: 2740
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Child window message loop

Post by TI-994A »

DontTalkToMe wrote:... When you free resources, this is not necessary reflected in the task manager in real time.
Some pages of memory results as allocated in the task manager until the memory manager trims pages...
That might be right. Not sure if there are any utilities to force this cache-trimming, but simply starting up some gobblers, like PhotoShop, might also do the trick. The memory usage, as reported by Task Manager, shrinks exponentially.

Good info. Thank you. :)
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
DontTalkToMe
Enthusiast
Enthusiast
Posts: 334
Joined: Mon Feb 04, 2013 5:28 pm

Re: Child window message loop

Post by DontTalkToMe »

TI, you can use EmptyWorkingSet to force the trimming of the pages not actually in use.
https://msdn.microsoft.com/it-it/library/windows/desktop/ms682606(v=vs.85).aspx

A tool you can use is VMMap.
https://docs.microsoft.com/en-us/sysinternals/downloads/vmmap
You can force the trimming using an option under Tools.

About the bind/unbind commands, they seem to work correctly when the same event is bound to different callbacks.
If you bind a single event to the same callback multiple times (why ?) and then try to unbind it, it does so only once, so n-1 bindings are still active.
Preferably this should not be permitted or at least treated appropriately, I think.
Last edited by DontTalkToMe on Wed May 30, 2018 3:19 pm, edited 1 time in total.
User_Russian
Addict
Addict
Posts: 1522
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: Child window message loop

Post by User_Russian »

This will move memory to swap file, but will not free.
User avatar
Blue
Addict
Addict
Posts: 966
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Child window message loop

Post by Blue »

Continuing on the subject of a child window closing when it loses focus
(running PB 5.62 (X64) on Windows 10 Pro x64)

I'm still using the.weavster's sample code, heavily modified by Ti-994A and some more by me. What follows is its latest incarnation, with the addition of a few debug statements to track what happens under certain circumstances :

Code: Select all


;- :: ************************************
;- ::    may 2018                               
;- ::    source : the.weavster  
;- ::    modified by TI-994A, Blue        
;- ::    child window 3                              
; https://www.purebasic.fr/english/viewtopic.php?p=522679#p522679
; demo : how to open an interactive child window WITHOUT using a secondary dedicated events loop
;- :: ************************************

EnableExplicit
; :: constantes            
Enumeration windows
  #main_WINDOW
  #child_WINDOW
EndEnumeration

Enumeration gadgets
  #main_btn 
  #main_clock
  #child_btn
  #child_clock
EndEnumeration

Enumeration timers
  #main_timer
  #child_timer
EndEnumeration
;.

Procedure Child_Open()
  If OpenWindow(#child_WINDOW,#PB_Ignore,#PB_Ignore,250,150,"child",#PB_Window_SystemMenu)
    ButtonGadget(#child_btn,10,10,150,30,"Message Box")
    TextGadget(#child_clock,20, 110, 210, 30,
               FormatDate("%hh:%ii", Date()), #PB_Text_Right)
    AddWindowTimer(#child_WINDOW, #child_timer, 1000)

    SetWindowData(#child_WINDOW,#True) ; signals that, when losing focus, the child window must close
    ResizeWindow(#child_WINDOW,WindowX(#main_WINDOW)+80,WindowY(#main_WINDOW)+80,#PB_Ignore,#PB_Ignore) ;so it remains visible
    SetWindowTitle(#child_WINDOW,"Closes spontaneously !")    ; show action in title 

    #timerTime = 20               ; timer duration 
    Global timer_t = #timerTime   ; timer countdown 
  EndIf
EndProcedure

Procedure Child_gadgets(gadget, eventType)
  Select gadget
    Case #child_btn
      ;##############################
      SetWindowData(#child_WINDOW,#False)   ; child window must NOT close when losing focus to message box
      SetWindowTitle(#child_WINDOW,"Remains active")
      MessageRequester("Message", "something real important... or not !",$40)
      ; reset the window to its auto-close state
      SetWindowData(#child_WINDOW,#True)    ; will close when losing focus
      SetWindowTitle(#child_WINDOW,"Closes spontaneously !")
      ;##############################      
  EndSelect
EndProcedure

; app's main window
Procedure Main_opened()
  If OpenWindow(#main_WINDOW,#PB_Ignore,#PB_Ignore,400,300,"Main window")
    ButtonGadget(#main_btn,10,10,150,30,"Show Child")
    TextGadget(#main_clock,280, 20, 100, 30,
               FormatDate("%hh:%ii:%ss", Date()), #PB_Text_Right)
    AddWindowTimer(#main_WINDOW, #main_timer, 1000)
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
EndProcedure

Procedure Main_gadgets(gadget, eventType)
  Select gadget
    Case #main_btn
      Child_Open()
  EndSelect
EndProcedure

;- ************************************
Procedure Events_Timer()
  Static currentMinute
  
  Select EventTimer()
    Case #main_timer
      ;updates every second
      SetGadgetText(#main_clock,
                    FormatDate("%hh:%ii:%ss", Date()))
    Case #child_timer
      ;updates every minute
      Define thisMinute = Minute(Date())
      If thisMinute <> currentMinute 
        currentMinute = thisMinute
        SetGadgetText(#child_clock, FormatDate("%hh:%ii", Date()))
      EndIf

      ; timer_t previously defined as global
      If timer_t <= 10
        SetGadgetText(#child_clock, "NotePad opens in " + timer_t + "...")
      EndIf
      timer_t - 1
      Debug "timer_t: "+ timer_t
      If timer_t = 0
        RunProgram("notepad.exe")
        timer_t = #timerTime                                        ; reset timer countdown to original value
        SetGadgetText(#child_clock, FormatDate("%hh:%ii", Date()))  ; re-display the clock
      EndIf
  EndSelect 
EndProcedure
Procedure Events_Gadget()
  Define gadget, eventType
  gadget = EventGadget()
  eventType = EventType()
  Select EventWindow()
    Case #main_WINDOW
      Main_gadgets(gadget,eventType)
    Case #child_WINDOW
      Child_gadgets(gadget,eventType)
  EndSelect
EndProcedure

Procedure Events_window_closed()
  ; closing main window quits app
  Debug "closing " + EventWindow() 
  If #main_WINDOW = EventWindow()
    Debug "  >>> " + #main_WINDOW + " is #main_WINDOW"
    Debug "all done !" 
    End
    Debug "never displayed ???" 
  EndIf
  
  Debug "  >>> " + #child_WINDOW + " is #child_WINDOW"
  CloseWindow(#child_WINDOW)
EndProcedure

Procedure Events_window_deactivated()
  Protected window = EventWindow()
  Select window
    Case #child_WINDOW
      If GetWindowData(window) = #True
        CloseWindow(window)

        ;SetActiveWindow(#main_WINDOW)       ; nothing happens !
        Debug "  >>> has #main_WINDOW changed to activated appearance ???"
      EndIf
  EndSelect
EndProcedure

Procedure Events_window_activated()
  Protected window = EventWindow()
  Debug "ActivateWindow " + EventWindow() 
  Debug "  >>> has window changed to activated appearance ???"
EndProcedure

BindEvent(#PB_Event_Gadget,@Events_gadget())
BindEvent(#PB_Event_Timer,@Events_timer())
BindEvent(#PB_Event_CloseWindow,@Events_window_closed())

BindEvent(#PB_Event_ActivateWindow,@Events_window_activated())
BindEvent(#PB_Event_DeactivateWindow,@Events_window_deactivated())

If Main_opened()
  Repeat
    WaitWindowEvent()
  ForEver
EndIf

End
The code works really well, but something is missing :
Launch the app, minimize PB's IDE, just leaving the debug output window opened. When you select PB's debug output window, it takes on its activated look, while the demo changes to its deactivated look. When you return to the demo, the reverse happens : its window takes on its activated look, while the debug window looks deactivated. All normal. That's the behaviour you expect and it happens correctly with any opened window on the desktop, and with the desktop itself.

However, If you now open a child window in the demo app, and then click into the parent window, the child window closes (as designed by code), but the parent window does not take on its activated look ! Furthermore, at line 144 of the code, the instruction SetActiveWindow(#main_WINDOW), intended to provoke a change of appearance, produces nothing; the debug statements also clearly show that the #PB_Event_ActivateWindow event never fires, even though it should (I think...). The parent window is expected to change its appearance to its activated colors.

Is this a PB bug ?
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
User avatar
TI-994A
Addict
Addict
Posts: 2740
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Child window message loop

Post by TI-994A »

Blue wrote:... If you now open a child window in the demo app, and then click into the parent window, the child window closes (as designed by code), but the parent window does not take on its activated look !
Remove the deactivation callback altogether, and replace the activation procedure with this one:

Code: Select all

Procedure Events_window_deactivated()

EndProcedure

Procedure Events_window_activated()
  Debug "ActivateWindow " + EventWindow() 
  If EventWindow() = #main_WINDOW 
    If IsWindow(#child_WINDOW) And 
       GetWindowData(#child_WINDOW) = #True
      CloseWindow(#child_WINDOW)
    EndIf
  EndIf
EndProcedure
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
Blue
Addict
Addict
Posts: 966
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Child window message loop

Post by Blue »

TI-994A wrote:[...]
Remove the deactivation callback altogether, and replace the activation procedure with this one:

Code: Select all

[,,,]
Thanks, TI-994A; what you propose does the job, no question. But it is exactly, word for word, what I already had in place as a workaround. (well, you know the saying about great minds thinking alike etc. :D )
So that's not really the question here. I'm not looking for a workaround. I already have that.
The point i want to raise is two-fold :
(1) in the original demo code, PB should have generated a #PB_Event_ActivateWindow event when a click in the parent window returns focus to it . Put another way, shouldn't PB be doing, with the demo code as is, exactly what the workaround code does ?
(2) on line 144 of the demo code, the SetActiveWindow(#main_WINDOW) instruction produces nothing . I certainly was expecting that instruction to force the parent window to show its activated colors. Weren't you ?

That's what I mean when I write that something appears to be missing in PB's output code.
And since PB fails on both points, isn't that a bug ? I don't see how the current behaviour can be the correct one.
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
User avatar
mk-soft
Always Here
Always Here
Posts: 6240
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Child window message loop

Post by mk-soft »

I only ever have one event loop and
in which nothing more is programmed than to leave the program.
Each gadget gets its own event procedure and each window gets its own event procedures for each event.

Most of the time I use my EventDesigner to create all necessary event procedures.
But with the Runtime-Library I found a new interesting method to bind the event procedures automatically.

Here is the link to the RuntimeEventManager with complete GUI event diagnosis.
Link: viewtopic.php?f=12&t=70119

Translated with www.DeepL.com/Translator
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Post Reply