WindowAddHotSpot - Silent Fail

Posted by Kathan on Fri 25 Jul 2014 07:00 AM — 5 posts, 17,464 views.

USA #0
Before I start, I just want to state that I've only been using MUSHClient for a week.

Version: 4.84

Problem: I was playing around with a blank miniwindow plugin that I created. I was using the Immediate window (Ctrl+I) and adding in text using the WindowText function. Worked perfectly.

Then I wanted to experiment with hotspots.

I used the WindowAddHotSpot function, and there were no errors, but there was no hotspot.


function MouseDown()
    Execute ( "say Hello!" )
end

txt = "(Hellow)"
txtlen = WindowTextWidth(win,font_id,txt)
WindowAddHotspot ( win, "hello" , (209-txtlen)/2, 170 , ((209-txtlen)/2)+txtlen , 170+12 , "", "", "MouseDown", "", "", "Salutations!", 1, 0)


I tried a code snippet I found on the forums:

-- show all hotspots
hotspots = WindowHotspotList(win)

if hotspots then
  for _, v in ipairs (hotspots) do 
    Note (v) 
  end
end -- if any


The only hotspots listed were the ones created inside the plugin itself. I tried adding the new hotspot code to the plugin and it worked as expected.

After playing around for a bit, I decided to try wrapping the WindowAddHotspot function in the check function. This time when running the code, I got the error "Hotspot processing must all be in same plugin".


Run-time error
World: Aardwolf
Immediate execution
[string "Immediate"]:6: Hotspot processing must all be in same plugin
stack traceback:
        [C]: in function 'error'
        [string "Check function"]:1: in function 'check'
        [string "Immediate"]:6: in main chunk


I think this is saying that all the functions the hotspots call have to be in one place (plugin or world script).

My complaint is I would have expected the error without using the check function. It was very frustrating to have the WindowAddHotspot function fail silently the way it did.

I also looked through the documentation and couldn't find any mention of the requirement for all the hotspots being in one place.
Amended on Fri 25 Jul 2014 07:05 AM by Kathan
Australia Forum Administrator #1
Quote:

My complaint is I would have expected the error without using the check function. It was very frustrating to have the WindowAddHotspot function fail silently the way it did.


It is pretty standard practice with API calls in many operating systems and utilities that they return a "success" or "failure" code (or throw an exception) rather than raising a dialog box or printing something to output.

It is usually the responsibility of the programmer to check documented return codes. Those return codes are mentioned in the documentation for the MUSHclient function APIs.

You could wrap a "check" call around every Windowxxxx call, however that gets a bit messy to read.

Another approach is to automatically replace all function calls with a "stub" function that does the check for you, as shown below.


-- automatically check Window* function results

-- stub function generator to check a function call

local function make_check_func (f)
  return function (...)
     local results = { f (...) }  -- call original function
     check (results [1])          -- check return value
     return unpack (results)      -- return results 
  end -- function
end -- make_check_func 

-- functions this will work with (that return eOK, and other things)

local valid_funcs = { }

for k, v in ipairs  {
"WindowAddHotspot", "WindowArc", "WindowBezier", "WindowBlendImage", 
"WindowCircleOp", "WindowCreate", "WindowCreateImage", "WindowDelete", 
"WindowDeleteAllHotspots", "WindowDeleteHotspot", "WindowDragHandler", 
"WindowDrawImage", "WindowDrawImageAlpha", "WindowFilter", "WindowFont", 
"WindowGetImageAlpha", "WindowGradient", "WindowHotspotTooltip", "WindowImageFromWindow", 
"WindowImageOp", "WindowLine", "WindowLoadImage", "WindowMergeImageAlpha", 
"WindowMoveHotspot", "WindowPolygon", "WindowPosition", "WindowRectOp", 
"WindowResize", "WindowScrollwheelHandler", "WindowSetPixel", "WindowSetZOrder", 
"WindowShow", "WindowTransformImage", "WindowWrite", 
} do valid_funcs [v] = true end 

-- see if one of them is a C function or a Lua function
local t = debug.getinfo (WindowShow, "S")

-- replace world Window* functions with a stub
if t.short_src == "[C]" then  -- if not already converted
  for k, f in pairs (world) do
    if type (f) == "function" and valid_funcs [k] then
      world [k] = make_check_func (f)  -- replace with stub
    end -- if
  end -- for
end -- if not done already


Put the above at the start of your world script file, or your plugin script.

Now you can call the functions normally, but an error will be raised if they fail. For example, testing (on the command line) with an non-existent window name:


/ WindowShow ("foo")


Result:


Run-time error
World: smaug2
Immediate execution
[string "Immediate"]:8: Requested miniwindow does not exist
stack traceback:
        [C]: in function 'error'
        [string "Check function"]:1: in function 'check'
        [string "Immediate"]:8: in function 'WindowShow'
        [string "Command line"]:1: in main chunk


There is a bit of complexity in the above because not every function call is documented to return eOK (zero) or something else, for example WindowInfo returns a variant which is the information requested. Thus there is a table above to limit the conversion of only certain functions to the stub calls.

Quote:

I also looked through the documentation and couldn't find any mention of the requirement for all the hotspots being in one place.


The last line (presently) in the documentation for WindowAddHotspot reads:


WARNING: The hotspot callbacks are not functions, but names of functions. That is, they should be supplied as string arguments. The supplied names are looked up in the script space at the appropriate time.


Each world file (and each plugin) has their own script space, indeed those scripts might be written in different languages. The wording "the script space" is supposed to imply "of the current plugin".

I have amended the documentation in the next release to emphasise that the callbacks have to be in the same plugin.
Amended on Fri 25 Jul 2014 11:00 PM by Nick Gammon
Australia Forum Administrator #2

A quick scan of the help database reveals that these are the functions (Window-related or not) which might return eOK.

Name
Accelerator
AcceleratorTo
AddAlias
AddFont
AddMapperComment
AddSpellCheckWord
AddTimer
AddToMapper
AddTrigger
AddTriggerEx
ArrayClear
ArrayCreate
ArrayDelete
ArrayDeleteKey
ArrayImport
ArraySet
CallPlugin
ChatAcceptCalls
ChatCall
ChatCallzChat
ChatDisconnect
ChatEverybody
ChatGroup
ChatID
ChatMessage
ChatNameChange
ChatPasteEverybody
ChatPasteText
ChatPeekConnections
ChatPersonal
ChatPing
ChatRequestConnections
ChatSendFile
ChatStopFileTransfer
CloseLog
Connect
DeleteAlias
DeleteAllMapItems
DeleteLastMapItem
DeleteTimer
DeleteTrigger
DeleteVariable
Disconnect
DoAfter
DoAfterNote
DoAfterSpecial
DoAfterSpeedWalk
DoCommand
EnableAlias
EnablePlugin
EnableTimer
EnableTrigger
Execute
FlushLog
GetAlias
GetTimer
GetTrigger
IsAlias
IsTimer
IsTrigger
LoadPlugin
LogSend
OpenBrowser
OpenLog
PlaySound
PluginSupports
Queue
ReadNamesFile
ReloadPlugin
ResetTimer
Save
SaveState
Send
SendImmediate
SendNoEcho
SendPkt
SendPush
SendSpecial
SetAliasOption
SetAlphaOption
SetBackgroundImage
SetChatOption
SetCommand
SetCommandSelection
SetCommandWindowHeight
SetCursor
SetCustomColourName
SetForegroundImage
SetOption
SetScroll
SetTimerOption
SetToolBarPosition
SetTriggerOption
SetVariable
SetWorldWindowStatus
ShiftTabCompleteItem
Sound
StopSound
TextRectangle
UdpListen
UnloadPlugin
WindowAddHotspot
WindowArc
WindowBezier
WindowBlendImage
WindowCircleOp
WindowCreate
WindowCreateImage
WindowDelete
WindowDeleteAllHotspots
WindowDeleteHotspot
WindowDragHandler
WindowDrawImage
WindowDrawImageAlpha
WindowFilter
WindowFont
WindowGetImageAlpha
WindowGradient
WindowHotspotTooltip
WindowImageFromWindow
WindowImageOp
WindowLine
WindowLoadImage
WindowMergeImageAlpha
WindowMoveHotspot
WindowPolygon
WindowPosition
WindowRectOp
WindowResize
WindowScrollwheelHandler
WindowSetPixel
WindowSetZOrder
WindowShow
WindowTransformImage
WindowWrite
WriteLog
USA #3
Ok, that makes sense. Thanks for the information.
Australia Forum Administrator #4
This isn't a bug BTW, it is doing what it is documented to do.