Liberty Basic is develeopped by Carl Gundel
Original Newsletter compiled by Alyce Watson
Translation to HTML: Raymond Roumeas

The Liberty Basic Newsletter - Issue #77 - JUL 2000

(c) 2000, Side by Side Software

http://alyce.50megs.com/sss/

All Rights Reserved

In this issue:

Simulating callbacks in Liberty BASIC - by Dennis McKinney

A new tool to place an LB program in the system tray - by Dennis McKinney


"Some people say I have attitude - maybe I do...but I think you have to. You have to believe in yourself when no one else does - that makes you a winner right there."

- Venus Williams


Dennis McKinney and the Liberty Belle

 

Many thanks to Dennis for the information contained in the following articles. Although he is fairly new to Liberty BASIC, Dennis is an accomplished programmer in other languages, and he brings that expertise, along with a great deal of enthusiasm to the Liberty BASIC online community. He maintains a Liberty BASIC-dedicated website. The URL follows. He writes clearly and explains things very well, and we are lucky to have him here. Thanks Dennis!

Contact Dennis McKinney:

mailto:dlm@mciworld.com

mailto:dlm81854@accs.net

The Liberty Belle:

http://belle2553.tripod.com/


Simulating callbacks to Liberty Basic

 

It has long been known that LB can not receive callback messages the way a windows program written in some other languages can. There is a way to work around this limitation in LB, it was originaly used by Brosco. The method he pioneered was to send a resize message to a 'callback' window then call his DLL from the LB resizehandler branch to get the message. The callback window used the 'resizehandler' command to know when to retrieve the message.

This method works very well all the way through LB version 1.42. The current alpha6 release does not respond to the wm_size message at this time.

Why would you want to use callbacks? They allow your LB program and programs written in other languages to communicate with each other. A good example of this is a system tray application. LB does not have the ability to utilize the system tray directly since it requires callbacks for the mouse messages.

To build a system tray application with LB requires a companion program written in a language that can process the callbacks from the tray and pass the information to your LB program. The companion program could be a DLL or an exe program.

The methods used apply to both types. A DLL could use the event to cause LB to call a function in the DLL to retrieve the message like Brosco did. If the companion program is an exe program it could use the methods that will be demonstrated here.

Many of us lack the knowledge to write DLL's so this demonstration will deal with a companion program written in visual basic. The VB program contains a small hidden window. This window reacts to the resize message in the same way as the LB callback window.

Here is a quick outline of one way to simulate callbacks between LB and another program.

Both programs have small hidden windows that are sent wm_size messages to cause them to react and read the callback message from the other program. The actual callback messages are placed in the title bar of the LB callback window. Each program uses the GetWindowText and SetWindowText API to do this. As soon as a 'callback message' is read, the program that reads it replaces that 'message' with an empty string to avoid unwanted duplicate actions. Then the program reading the message can take whatever action is required for that message. Both programs need to obtain each others handles before these API calls can be made. A simple way to do this is to create files at runtime that contain the handle of each program and read it into memory at the start of each program.


Building a Liberty Basic system tray program

The companion system tray program used here is named LBtray.exe. It is available at http://belle2553.tripod.com/ and includes the code listed here plus the skeleton source code to make building your own system tray applications easy.

 

The example program shown here will use the system tray abilities that LBtray provides to the Liberty Basic programmer.

  1. Two way runtime communication between Lb and the system tray.
  2. Optional user defined tray menus, right single click and left double click.
  3. Tray menu items are not limited. (Assuming nobody wants 32,000 of them).
  4. Display a single tray icon.
  5. Change the tray icon.
  6. Animate the tray icon.
  7. Change to different animations.
  8. Change animation speeds.
  9. Stop animation
  10. No practical limit for the number of icons or animation sequences.
  11. Change the tray icon's tooltip messages as desired.
  12. Pop up as many different LB windows as needed.
  13. Hide the tray icon when LB windows are visible, if desired.
  14. Full tray menuitem manipulation by API, enable, disable, grayed, checked, etc.

An explanation of the setup file requirements, LBtray commands, and callback messages is included at the end of the sample program listing.

To start with, define the major variables and arrays to use.

 
 'Constants:
    True = 1      'not used yet, just here to be handy
    False = 0     'same thing
    Left = 0	'left menu index
    Right = 1	'right menu index
    '--------
 'Major variables:
    HndLBtray = 0   'will receive hwnd of LBtray.exe
    WinHnd = 0      'hwnd variable used for calls
    Hcb = 0         'handle of callback window
    Mode = 0        'message parameter for [ShowWindow]
    SubMenu = 0     'submenu parameter for menu API calls, left or right
    MenuItem = 0    'menuitem parameter for menu API calls
    MenuFlag = 0    'parameter for menu API calls
    wMsg = 0	  'message parameter for SendMessage API
    '--------
 
 'Arrays
    Dim Info$(10,10)   'for calls to Files
    Dim SubMenu(2)     'handles for two submenus.
    Dim MenuItem(2,11) 'handles for menu items in two submenus
                       'dimension the 2nd index to accomodate the
                       'larger of the two menus
    Dim Ttip$(3)       'array for tooltips (not required)
    '--------
 
    CurIcon = 0	'current icon index (not required)
    CurTT = 0	'current tooltip index (not required)
 
    Ttip$(0) = "A Message From Liberty Basic"
    Ttip$(1) = "Second LB Tool Tip"
    Ttip$(2) = "Third LB Tool Tip"

The first window we need in our tray program is the callback window. This has to be a window of type 'window'. Make it small to avoid the 'flash' it would make on the screen before it is hidden.

 
    nomainwin
 
    WindowWidth = 1
    WindowHeight = 1
    UpperLeftX = 1
    UpperLeftY = 1
 
    Open "Callback" for Window as #CB
    Hcb = hwnd(#CB) 'store permanently
 

As soon as it is created it should be hidden before any other windows are created. Then set the resizehandler event to this window.

 
    Open "user.dll" for dll as #user 'leave open till the program ends.
 
    Mode = _SW_HIDE
    WinHnd = hwnd(#CB)
    Gosub [ShowWindow] 
    Print #CB, "resizehandler [HandleResize]"
 

Now create the main window(s) of the program. Since this is a tray program, hide it also. I use a graphics window here just to demonstrate the way to refer to a graphics window or text window handle.

 
    WindowWidth = 300
    WindowHeight = 300
    UpperLeftX = int((DisplayWidth-WindowWidth)/2)
    UpperLeftY = 100
 
    Textbox #Test1.tb, 10, 50, 100, 25
 
    Open "A Liberty Basic System Tray Program" for Graphics_nsb as #Test1
    Print #Test1, "Trapclose [ReturnToTray1]"
    Print #Test1, "fill lightgray"
    Print #Test1, "flush"
 
    WinHnd = hwnd(#Test1)
 
    '-------------------
    'Use GetParent for graphic or Text windows only.
    CallDll #user, "GetParent", _
       WinHnd AS word, _
       ParHnd AS word
    
    WinHnd = ParHnd
    '-------------------
 
    Gosub [ShowWindow] 
 

Notice the trapclose event handler. [ReturnToTray1] will not close the application, it will only hide it and show the tray icon again. The means to exit will be provided from the system tray.

The initial communication between our applcation and the VB program is done with an initialization file. In this file specify the handle of the callback window and other info for the mouse menus and icons to use. When the VB program starts it reads this file and uses the info for it's own setup.

 
    'create the initialization file LBtray.ini
    Gosub [InitLBtray]
 
    'start the VB program so it can read the LBtray.ini file and display
    'the icon in the system tray
    Run "LBtray.exe"
 

Read the file that is created by LBtray.exe when it starts that contains the handle of LBtray.exe.

 
    'get the handle for LBtray.exe
    Gosub [GetMsg]
 

Once the handle to LBtray is obtained from the file it can be used for all further calls to send messages or to obtain information about LBtray.

Now fill the MenuItem array with the handles of the menu items for the two menus that LBtray can display.

 
    Gosub [InitMenuArray]
 
 [Main.Loop]
    Input A$
 Goto [Main.Loop]
 
 [Exit]
    Gosub [CloseLBtray]
    Close #user
    Kill "LBtray.ini"
    Close #CB
    Close #Test1
    End
 

The rest of the program consists of the sub routines to initialize LBtray, send commands, receive and process callback messages.

 

'___________________________________________________________________________
 
 [ShowWindow] 'Show or hide a window
 
    CallDll #user, "ShowWindow", WinHnd as word, Mode as ushort, result As
word
 
 Return
'___________________________________________________________________________
 
 [InitLBtray] 'This is where all initial info is sent to LBTray.
 
  'Printing to the file must follow this sequence.
  ' hwnd of the callback window , icons, tooltip message, left menu, right
menu.
 
    Open "LBtray.ini" for output as #1
        Print #1, Hcb               'handle of the LB Callback window.
        Print #1, "Icons"           'identifier.
        Print #1, 12                'number of icons to use.
        Print #1, 200               'initial animation frame rate.
        Print #1, "test.ico"    'index 0   The icon files to use.
        Print #1, "moon1.ico"        ' 1
        Print #1, "moon2.ico"        ' 2
        Print #1, "moon3.ico"        ' 3
        Print #1, "moon4.ico"        ' 4
        Print #1, "moon5.ico"        ' 5
        Print #1, "moon5.ico"        ' 6
        Print #1, "moon7.ico"        ' 7
        Print #1, "moon8.ico"        ' 8
        Print #1, "book1.ico"        ' 9
        Print #1, "book2.ico"        ' 10
        Print #1, "book3.ico"        ' 11
 
        Print #1, "A Message From Liberty Basic"  'Default Tooltip message.
 
        Print #1, "Lmenu"            'Left menu identifier.
        Print #1, 3                  'number of items in left menu.
        Print #1, "About"            ' L0 (returned value). The number is
the menu index number.
        Print #1, "-"			 ' index will not be returned for separator bars
        Print #1, "Exit"             ' L2
 
        Print #1, "Rmenu"            'Right menu identifier.
        Print #1, 11                 'number of items in right menu.
        Print #1, "Show LB program"  ' R0 (returned value).
        Print #1, "-"
        Print #1, "First Animation"  ' R2
        Print #1, "Second Animation" ' R3
        Print #1, "Fast Animation"   ' R4
        Print #1, "Slow Animation"   ' R5
        Print #1, "Stop Animation"   ' R6
        Print #1, "-"
        Print #1, "Change Icon"      ' R8
        Print #1, "-"
        Print #1, "Change Tooltip"   ' R10
    Close #1
 
 Return
'___________________________________________________________________________
 
 [CloseLBtray]
 
    wMsg = _WM_CLOSE
    Gosub [NotifyLBtray]
 
 Return
'___________________________________________________________________________
 
 [InitMenuArray]
 'This routine will fill the MenuItem array with the menuitem handles of both
 'menus regardless of how many items are used. It also fills the SubMenu array
 'with the handles of LBtray's sub menus.
 
    CallDll #user, "GetMenu", HndLBtray as word, HMnuBar as word
 
    'LBtray allows two submenus. Their indexes are
    '0 for the left menu and 1 for the right menu.
 
    For MPos = 0 to 1
        CallDll #user, "GetSubMenu", HMnuBar as short, MPos as short,
HndSubMnu as short
        SubMenu(MPos) = HndSubMnu
        CallDll #user, "GetMenuItemCount", HndSubMnu as short, MiCnt as word
        For IC = 0 to (MiCnt - 1)
            CallDll #user, "GetMenuItemID", HndSubMnu as word, IC as short,
ID as word
            MenuItem(MPos,IC) = ID
        Next IC
    Next MPos
 
 Return
'___________________________________________________________________________
 
 [EnableMenuItem] 'enable, disable, or gray/disable a menu item
 'This routine is only one of the many menu manipulations available via API.
 'It's probably the one you will use the most. See [PopUp.1] and
[ReturnToTray1].
 
    'get the handle of the correct submenu
    HndSubMnu = SubMenu(SubMenu)
    'get the handle of the correct menu item
    MenuID = MenuItem(SubMenu,MenuItem)
 
    CallDll #user, "EnableMenuItem", _
        HndSubMnu as word, MenuID as word, MenuFlag as word, Result as word
 
 Return
'___________________________________________________________________________
 
 [HandleResize]
  'this routine executes when the LB callback window gets a WM_SIZE message
 
    'get the message from the callback window title bar
    Gosub [GetMsg]
 
    If Msg$ <> "" Then
        'Clear the title bar
        Clr$ = ""
        CallDll #user, "SetWindowText", Hcb as word, Clr$ as ptr, Result as
void
        Gosub [ProcessMsg]
    End If
 
    If Msg$ = "" Then
        'Handle any resize events not caused by a callback here. Such as a
user
	'resizing the window.
    End If
 
 Goto [Main.Loop]
'___________________________________________________________________________
 
 [GetMsg]
 'when the callback window is first hidden it will trigger the resize handler.
 'Since LBtray will not be created at that time don't try to process messages 
 'until the handle to LBtray has been retrieved.
 
    If HndLBtray <> 0 Then
        Msg$="                               " + Chr$(0)
        Length = Len(Msg$)
        CallDll #user, "GetWindowText", Hcb as word, Msg$ as ptr, _
            Length as ushort, Result as ushort
        Msg$=Left$(Msg$,Result)
        wMsg = _WM_SIZE
    end if
 
    'get the handle for LBtray if we don't have it yet
    If HndLBtray = 0 Then
        Files DefaultDir$, "Hndl.inp", Info$(
        If val(Info$(0, 0)) > 0 then
            'get the handle for LBtray.
            Open "Hndl.inp" for input as #1
                Input #1, HndLBtray$
                HndLBtray = val(HndLBtray$)
            Close #1
        End If
    End If
 
 Return
'___________________________________________________________________________
 
 [ProcessMsg]  'handle the callback message from LBtray
  'when a tray menu item is clicked LBtray will place a string (text) in
  'the title bar of the LB callback window. The string will start with
  'either 'L' for the left menu or 'R' for the right menu. The letter
  'will be followed by a number representing the index number of the
  'menu item that was clicked. The range of numbers is determined by
  'the number of menu items you specify in [InitLBtray]. The only other
  'message returned might be 'LDCLICK'.
 
    If Msg$ = "L0" Then Gosub [About]
    If Msg$ = "L2" Then Gosub [Exit]
 
    If Msg$ = "R0" Then Gosub [PopUp.1]
    If Msg$ = "R2" Then
        First = 9 'index of first icon to animate
        Last = 11 'index of last icon to animate
        Gosub [Animate]
    End If
    If Msg$ = "R3" Then
        First = 1
        Last = 8
        Gosub [Animate]
    End If
    If Msg$ = "R4" Then Gosub [Fast]
    If Msg$ = "R5" Then Gosub [Slow]
    If Msg$ = "R6" Then Gosub [Stop]
    If Msg$ = "R8" Then Gosub [ChangeIcon]
    If Msg$ = "R10" Then Gosub [ChangeTT]
 
    If Msg$ = "LDCLICK" Then
          'Only returned if there is no left menu.
          Gosub [Exit]
    End If
 
 Return
'___________________________________________________________________________
 
 [SendUserMsg] 'send a valid message to LBtray
 
    calldll #user, "SetWindowText", Hcb as word, NewMsg$ as ptr, Result as
void
 
    Gosub [NotifyLBtray]
 
 Return
'___________________________________________________________________________
 
 [NotifyLBtray] 'let LBtray know a message has been sent. The message is
_WM_SIZE, 
	'contained in wMsg. The only other message sent is _WM_CLOSE, sent at the
	'ending of the LB program.
 
    CallDll #user, "SendMessage", HndLBtray as word, wMsg as word, _
        wParam as word, lParam as long, Result as long
 
 Return
'___________________________________________________________________________
 
 [PopUp.1] 'show an LB window
 
    Mode = _SW_SHOW
    Gosub [ShowWindow]
    Print #Test1.tb, "!setfocus"
 
    NewMsg$ = "HideIcon"
    Gosub [SendUserMsg]
 
   'If you just want to disable this menu item while your LB window is
visible then
   'use the next four lines instead of the last 2 lines above. Make the
same changes
   'in the branch [ReturnToTray1].
 
   ' SubMenu = Right     'R0 was returned from LBtray
   ' MenuItem = 0
   ' MenuFlag = _MF_GRAYED
   ' Gosub [EnableMenuItem]
 
 Return
'___________________________________________________________________________
 
 [ReturnToTray1]
 
    Mode = _SW_HIDE
    Gosub [ShowWindow]
 
    NewMsg$ = "Restore 0"
    Gosub [SendUserMsg]
 
   ' SubMenu = Right
   ' MenuItem = 0
   ' MenuFlag = _MF_ENABLED
   ' Gosub [EnableMenuItem]
 
 Goto [Main.Loop]
'___________________________________________________________________________
 
 [About]
 
    Text$ = "LBtray is a Liberty Basic companion that provides" + chr$(13) + _
            "full runtime system tray features for" + chr$(13) + _
            "Liberty Basic programs"
 
    Notice "About LBtray" + chr$(13) + Text$
 
 Return
'___________________________________________________________________________
 
 [Animate]
 
        NewMsg$ = "Animate" + " " + str$(First) + " " + str$(Last)
        Gosub [SendUserMsg]
 
 Return
'___________________________________________________________________________
 
 [Fast]
 
    NewMsg$ = "SeqTime 200"
    Gosub [SendUserMsg]
 
 Return
'___________________________________________________________________________
 
 [Slow]
 
    NewMsg$ = "SeqTime 1000"
    Gosub [SendUserMsg]
 
 Return
'___________________________________________________________________________
 
 [Stop]
 
    NewMsg$ = "EndAni"
    Gosub [SendUserMsg]
 
 Return
'___________________________________________________________________________
 
 [ChangeIcon]
 
        CurIcon = CurIcon + 1
        If CurIcon > 11 then CurIcon = 0
        NewMsg$ = "ShowIcon" + " " + str$(CurIcon)
        Gosub [SendUserMsg]
 
 Return
'___________________________________________________________________________
 
 [ChangeTT]
 
    CurTT = CurTT + 1
    If CurTT > 2 Then CurTT = 0
 
    tt$ = Ttip$(CurTT)
 
    NewMsg$ = "CttMsg" + " " + tt$
    Gosub [SendUserMsg]
 
 Return
'___________________________________________________________________________

End of program listing

Details of initialization file settings, runtime commands for the LB program to send to LBtray, and callback messages from LBtray.

Commands are not case sensitive.

=================================================================
 
Info to be printed to "LBtray.ini" must be in the order shown.
 
 Required       Type         Info
-----------------------------------------------------------------
 Yes            Number       Handle of the window used to send and 
                             recieve messages.
 
 Yes            String       "icons"  (literal string)
 Yes            Number       Number of icons to load. Minimum is 1.
 Yes            Number       Animation sequence time (1000 = 1 second).
                             Minimum is 100. Maximum is 65,535.
 Yes            String       Icon name (include path if in sub dir, 
                             ie DefaultDir$ + "\ico\myicon.ico").
                             Use one entry for each icon name.
                             Icons listed here will be assigned an 
                             index number beginning with 0. Bitmaps
                             may not be used.
 
 Yes            String       Tooltip message for the tray icon. Maximum
                             length of the message is 64 characters.
 
 Yes            String       "Lmenu"  (literal string)
 Yes            Number       Number of items in left mouse button menu
                             including separator bars. 
                             Use 0 if no left menu is desired.
                             NOTE: If 0 is specified then double 
                             clicking on the the tray icon with the 
                             left mouse button will cause a message of
                             "LDCLICK" to be sent to Hcb.
 See above      String       Menu item. Can not include ampersand.
                             Use one entry for each menu item. 
                             Separator bars are entered as "-"
 
 Yes            String       "Rmenu" (literal string)
 Yes            Number       Number of items in right mouse button 
                             menu including separator bars. 
                             Use 0 if no right menu is desired.
 No             String       Menu item. Can not include ampersand.
                             Use one entry for each menu item. 
                             Separator bars are entered as "-"
 

When a menu item is clicked, a letter and number will be printed to the Hcb title bar. The letter will be L for left or R for right.

The letter will be followed by the index number of the menu item. ie "L2" or "R0" etc. Any separator bars you specify will increase the index number of the next menu item by 1.

Index numbers start at 0, not 1. If you list your menu items as

			"Exit"
			"-"
			"Menu 1"
			"-"
			"Menu 2"
			"Menu 3"

then the index number for "Exit" is 0, "Menu 1" is 2, "Menu 2" is 4, "Menu 3" is 5. Index numbers for "-" will not be returned.

Here's an example of an LBtray.ini setup.

 
    Hcb = hwnd(#CallbackWindow)
 
    Open "Lbtray.ini" for output as #LBini
        print #LBini, Hcb         'Handle of the window to send and recieve
messages.
 
        print #LBini, "icons"
        print #LBini, 3           'Number of icons you are using.
        print #LBini, 200         'Animation sequence time. Experiment.
        print #LBini, "test1.ico" 'This icon will have an index of 0.
        print #LBini, "test2.ico" 'This icon will have an index of 1.
        print #LBini, "test3.ico" 'This icon will have an index of 2.
 
        print #LBini, "A Message From Liberty Basic"  'The tooltip message
 
        print #LBini, "lmenu"
        print #LBini, 3                   'The number of items in the left
menu.
        print #LBini, "LB Left Menu 1"    'This will have an index of 0.
        print #LBini, "-"                 'Separator bar
        print #LBini, "LB Left Menu 2"    'This will have an index of 3.
 
        print #LBini, "rmenu"
        print #LBini, 1
        print #LBini, "LB Right Menu 1"	'This will have an index of 0.
    Close #LBini
 

When your program starts LBtray the first icon listed will be displayed in the system tray.

==================================================================
 
 COMMANDS      LBTray will process these commands. The entire command is
               a string (text).
 
 Command			Info
-----------------------------------------------------------------
 
 "HideIcon"             Removes the icon from the system 
                        tray. Use this command when your 
                        program is no longer minimized to 
                        the system tray, but might be 
                        minimized again.
 
 "Restore X"            Restores the system tray icon. Use
                        after using the 'HideIcon' command.
                        X = index of the icon to display.
                        Must be an icon listed in LBTray.ini.
                        If animation was in progress at the
                        time 'HideIcon' was used, that animation
                        sequence will continue after the single
                        icon has been displayed. You may want to
                        keep track of any animation sequence in
                        effect and specify the first icon of that 
                        sequence for the X parameter.
 
 "ShowIcon X"           Where X = index of the icon. Must be an
                        icon listed in LBTray.ini. 
                        Displays a single icon in the 
                        system tray. Will stop any animation.
 
 "Animate A B"          Starts or changes an animation sequence.
                        A = the starting animation icon.
                        B = the ending animation icon.
                        Any range of animation icons may 
                        be specified as long as they 
                        have been listed in LBTray.ini. 
                        "Animate 3 5" will cycle the 
                        animation icons whose index is 
                        3, 4, and 5 in a loop. Make sure the
                        number specified first is smaller than
                        the second number.
 
 "EndAni"               Stops animation. Should be 
                        followed by "ShowIcon X" to 
                        display the icon you want.
 
 "CttMsg X"             Changes the Tooltip Message.
                        X = the new message. 
                        64 characters maximum length.
 
 "SeqTime X"            Changes the animation sequence timing.
                        X = time. 1000 is about 1 second.
                        The maximum is 65,535 or just over
                        1 minute.
=================================================================
 

CALLBACK MESSAGES

LBTray will return these messages to the Hcb window.

 
 Message		Info
-----------------------------------------------------------------
 "LX"			L means the left menu. X is the index number of 
		 	the menu item. 
 
 "RX"			R means the right menu. X is the index number of 
		 	the menu item.
 
 "LDCLICK"		This will be returned only if no left menu exists
		 	and the user double clicks the icon with the left
		 	mouse button.
 

 


Comments, requests or corrections: Hit 'REPLY' now! Dean Hodgson -- mailto:Hodgson.Dean@saugov.sa.gov.au Alyce Watson -- mailto:awatson@wctc.net