IRC client
A new bug fix release is out, mostly minor fixes which you can see in the changelog. There is one new feature though that I am rather excited about; There are new Lua bindings for scripts thanks to @mniip. If you are a scripter I’ll go more into detail on what makes this great…
Just some quick background information: the plugin supports lua[jit] 5.1-5.3, is shipped on all platforms, and the documentation can be found here.
The first thing that really stands out when switching from Python scripts to Lua ones is the resource usage. I have around 10 scripts loaded at any given time and the python plugin alone would use around 60MB of ram, when I rewrote every script in Lua that number decreased to about 4MB. On top of that luajit is an extremely fast interpreter though scripts are rarely CPU bound.
Another useful feature is that Lua is extremely portable, this means that Windows users do not need to deal with the hassle of downloading a giant Python installer that ends up broken, requires rebooting, or is incompatable with a script. The Windows installer will include all of Lua itself which is effectively a 1MB single binary and will be installed by default so scripters can be confident users have it.
Lastly and my favorite feature is the ability to use a library called lgi. GObject-Introspection is a technology that allows information about C libraries to be used externally (usually from other languages). This means that scripts can integrate with the same libraries HexChat uses itself such as GLib and Gtk. This library is small enough that we will be bundling it on Windows which means for the first time all Windows users can have scripts that can easily modify the UI or integrate with the mainloop.
Here are some basic examples that should give you an idea of more advanced things you can do with this ability:
local lgi = require('lgi')
local GLib = lgi.require('GLib')
local Gio = lgi.require('Gio')
hexchat.register('FileTest', '1', 'Opens a file async')
--[[ A common problem in scripts is that they have to do blocking IO
operations and in a graphical application like HexChat this means
locking up the client. To avoid this GLib provides many asynchronous
APIs that use threads and the mainloop to avoid blocking. This is just
an example of opening a large file without blocking. ]]--
hexchat.hook_command('readfile', function (word, word_eol)
if #word < 2 then
print('Need a file name')
return hexchat.EAT_ALL
elseif not GLib.path_is_absolute(word[2]) then
print('Need an absolute path')
return hexchat.EAT_ALL
end
local file = Gio.File.new_for_path(word[2])
local original_context = hexchat.props.context
-- This starts another thread and calls the function when it is finished
file:load_contents_async(nil, function (file, result)
local contents, etag, err = file:load_contents_finish(result)
-- We need to manually reset the context as it may have changed
if not original_context:set() then
hexchat.find_context():set() -- front
print('Original context lost')
end
if err then
print('Error reading file: ' .. tostring(err))
else
print('File contained: ' .. #contents .. ' bytes')
end
end)
return hexchat.EAT_ALL
end)
local lgi = require('lgi')
local Gtk = lgi.require('Gtk', '2.0')
hexchat.register('WinTile', '1', 'Tile the window left/right')
--[[ HexChat being a graphical application many times you want scripts
to modify the UI, this is just a simple script that takes a reference
to the main window and modifies it in a simple way (moving and resizing).
In practice you can do any operation you would like including spawning
new windows and modifying widgets. ]]--
hexchat.hook_command('tile', function (word, word_eol)
if #word < 2 then
print('Need side, left, right, or reset')
return hexchat.EAT_ALL
end
local side = word[2]:lower()
local window = Gtk.Window(hexchat.get_info('gtkwin_ptr'))
if side == 'reset' then
local width, height = hexchat.prefs['gui_win_width'], hexchat.prefs['gui_win_height']
local x, y = hexchat.prefs['gui_win_left'], hexchat.prefs['gui_win_top']
window:resize(width, height)
window:move(x, y)
hexchat.command('set -quiet gui_win_save on')
elseif side == 'left' or side == 'right' then
local screen = window:get_screen()
local width, height = screen:get_width(), screen:get_height()
hexchat.command('set -quiet gui_win_save off') -- We don't want to save the temporary size
window:resize(width / 2, height)
if side == 'left' then
window:move(0, 0)
else
window:move(width / 2, 0)
end
else
print('Invalid side. Need left, right, or reset')
end
return hexchat.EAT_ALL
end)