AwesomeWM: Closing the prompt box in code without pressing Enter/Escape - lua

I'm trying to add a keybinding to my rc.lua for shutting down my computer, which will display a prompt like "Shutdown (y/n)? ", and will call a shutdown if "y" is pressed, but will close if anything else is pressed. Here's my attempt so far:
...
awful.key({ modkey, "Control", "Shift" }, "q",
function ()
awful.prompt.run {
prompt = "Shutdown? (y/n) ",
textbox = awful.screen.focused().mypromptbox.widget,
keypressed_callback = function (_, key, _)
if key == "y" then
naughty.notify {
text = "Shutting down!"
}
else
naughty.notify {
text = "Not shutting down!"
}
end
return
end,
}
end,
{description = "shutdown", group = "awesome"}),
...
However, the prompt remains active after keys are pressed - the keypressed_callback will continue to trigger every another key is pressed, until I press either Return or Escape.
This is sensible default behaviour, but in my case I want the prompt to close after the first keypressed_callback event. My first thought was to use a return within the keypressed_callback to try to escape/cancel/destroy the prompt, but that isn't doing anything.
Is there anyway to achieve this?

You can call awful.keygrabber.stop().
I must admit this isn't ideal and that function is actually deprecated. I think this is indeed a feature gap in the prompt module itself.
What I would propose for single character prompts is to use https://awesomewm.org/apidoc/core_components/awful.keygrabber.html directly and implement a non-interactive widget using the textbox.
(another alternative is to use root.fake_input or awful.keyboard to emulate Escape/Enter, but that's a very bad hack)

Related

Changing contents of global variables in a Lua script for Awesome Window Manager?

So I've been trying to configure my Awesome WM config (rc.lua) to detect if my IBM model M13 is connected to my laptop upon login/reset. This is to change what the modkey should be since the M13 doesn't have a super key.
The following code makes sense to me and changes modkey within the function being made for the awful.spawn.easy_async function, but after finishing the modkey changes back to Mod4.
modkey = "Mod4"
awful.spawn.easy_async(
"xinput list",
function(stdout, stderr, reason, code)
local msg = "Regular keyboard Modkey = Super"
-- Debug notification that shows that the modkey is
-- at its default for the superkey Mod4
naughty.notify({
text = modkey,
timeout =7
})
if code ~= 0 then
msg = "Missing xinput to see devices\nModkey = Super"
elseif stdout:match("CHESEN") == "CHESEN" then
-- CHESEN is my PS/2 to USB adapter
msg = "IBM M13 detected\nModkey = Alt"
modkey = "Mod1" -- Sets new modkey to Alt
end
-- Notification message
naughty.notify({
text = msg,
timeout =7
})
end
)
-- Debug notification to verify key but key goes back to Mod4
naughty.notify({
text = modkey,
timeout =7
})
The output can be seen here. It doesn't print the notifications in order but the prints of Mod 4 are both of the debug prints.
Notification Output
I don't use Lua much aside from changing my configs from time to time so I'm having difficulty understanding how my global variable modkey can be changed with out it resetting. Other methods I tried was to have the function defined as a function I called setModKey to be passed as a parameter to easy_async and I tried setting modkey using _G to set it as _G.modkey, but I end up getting the same result.
Am I missing something fundamental to Lua or is this affected by how Awesome WM utilizes Lua? Any help will be very appreciated.
Use io.popen instead of awful.spawn.easy_async. Yes, normally using io.popen is really not recommended, but here the following happens:
Awesome starts
You call easy_async to capture the output of xinput list
Since it is async, your config continues to be loaded, so e.g. all your keybindings are set
easy_async does its job and you set modkey to something else.
This means that any keybinding which will be defined from now on use the new modkey, but all already-existing keybindings are not modified by this. So, basically nothing happens.
And for your debugging calls to naughty.notify: The one after the function is triggered first and only then, later, the inner one triggers. So it does not go back, but instead you first show the old value and only later the new one.

Command+DELETE no longer working on Hammerspoon once the app focus switches to another

I'm now writing my own init.lua on Hammerspoon, and would like to remap some of my kews to others. Specifically, I would like to switch backslash key and delete key on my macOS.
However, while the simple delete and backslash works properly, once I switch my app focus to another, type in something there, and go back to the original app, the delete key (actually backslash key as I switched it) gets longer working; instead it deletes the characters in the app before it aborts the focus (i.e. the second to last focused app). But if I type in something there and then try again to delete it, suddenly the deletion works again without any problems.
I wonder why it is suddenly not working; it might be a bug, though. Anyway here is my init.lua to switch delete and backslash.
local VK_BACKSLASH = 0x2a
local VK_DELETE = 0x33
keyEventtap = hs.eventtap.new({
hs.eventtap.event.types.keyDown
}, function(event)
local bundleId = string.lower(hs.application.frontmostApplication():bundleID())
local keyCode = event:getKeyCode()
local flags = event:getFlags()
if keyCode == VK_DELETE then
if flagsMatches(flags, {'shift'}) then
event:setKeyCode(VK_BACKSLASH)
event:setFlags({shift=true})
else
event:setKeyCode(VK_BACKSLASH)
end
elseif keyCode == VK_BACKSLASH then
event:setKeyCode(VK_DELETE)
end
end)
keyEventtap:start()
What am I missing here...?
You must add keyUp and flagsChanged event to monitor the event tap. So, instead of:
keyEventtap = hs.eventtap.new({
hs.eventtap.event.types.keyDown
}, function(event)
change to the following:
keyEventtap = hs.eventtap.new({
hs.eventtap.event.types.keyDown,
hs.eventtap.event.types.flagsChanged,
hs.eventtap.event.types.keyUp
}, function(event)
At least now this is working for me.

Only show title bar on floating windows

In awesome 4.0, is there a way to only display the titlebar on floating windows?
Looking at the docs, there doesn't seem to be an option out of the box.
To specify; I'm looking for a solution that work when I dynamically switch windows between tiling and floating.
A bit late, but I wanted to do this too and I got it mostly working. It doesn't cover all the cases when you'd expect a client to show or hide its titlebar, but it's close enough for my use case.
It's rather simple, first you need to disable titlebars for every client, so add titlebars_enabled = false in the properties of the default rule matching all clients.
Then, when a client becomes floating you need to toggle on his titlebar, and toggle it off when it stops floating.
I wrote this little helper function to make the code clearer. It's rather simple, if s is true then show the bar, hide it otherwise. But there's a catch, in our case the windows never had a titlebar so it isn't created yet. We send the signal to have one built for us if the current one is empty.
-- Toggle titlebar on or off depending on s. Creates titlebar if it doesn't exist
local function setTitlebar(client, s)
if s then
if client.titlebar == nil then
client:emit_signal("request::titlebars", "rules", {})
end
awful.titlebar.show(client)
else
awful.titlebar.hide(client)
end
end
Now we can hook the property change:
--Toggle titlebar on floating status change
client.connect_signal("property::floating", function(c)
setTitlebar(c, c.floating)
end)
But that only applies to clients that changes states after being created. We need a hook for new clients that are born floating or in a floating tag:
-- Hook called when a client spawns
client.connect_signal("manage", function(c)
setTitlebar(c, c.floating or c.first_tag.layout == awful.layout.suit.floating)
end)
And finally, if the current layout is floating, clients don't have the floating property set, so we need to add a hook for layout changes to add the tittlebars on clients inside.
-- Show titlebars on tags with the floating layout
tag.connect_signal("property::layout", function(t)
-- New to Lua ?
-- pairs iterates on the table and return a key value pair
-- I don't need the key here, so I put _ to ignore it
for _, c in pairs(t:clients()) do
if t.layout == awful.layout.suit.floating then
setTitlebar(c, true)
else
setTitlebar(c, false)
end
end
end)
I didn't want to spend to much time on this so it doesn't cover cases where a client gets tagged in a floating layout, or when a client is tagged multiple times and one of those tag is floating.
Change
{ rule_any = {type = { "normal", "dialog" }
}, properties = { titlebars_enabled = true }
},
to
{ rule_any = {type = { "dialog" }
}, properties = { titlebars_enabled = true }
},
Niverton's solution works very well for simply switching from tiling to floating modes; however, floating windows will lose their titlebar when maximized and then unmaximized. To fix this, a better solution would be to replace
client.connect_signal("property::floating", function(c)
setTitlebar(c, c.floating)
end)
with
client.connect_signal("property::floating", function(c)
setTitlebar(c, c.floating or c.first_tag and c.first_tag.layout.name == "floating")
end)
This should fix the issue so that windows can be properly maximized without having to switch to tiling mode and back to get the titlebars again.
I found this general idea on a reddit post about the subject, provided by u/Ham5andw1ch. I have just simplified the code using Niverton's proposed function and some short-circuit logic.

readByteSync - is this behavior correct?

stdin.readByteSync has recently been added to Dart.
Using stdin.readByteSync for data entry, I am attempting to allow a default value and if an entry is made by the operator, to clear the default value. If no entry is made and just enter is pressed, then the default is used.
What appears to be happening however is that no terminal output is sent to the terminal until a newline character is entered. Therefore when I do a print() or a stdout.write(), it is delayed until newline is entered.
Therefore, when operator enters first character to override default, the default is not cleared. IE. The default is "abc", data entered is "xx", however "xxc" is showing on screen after entry of "xx". The "problem" appears to be that no "writes" to the terminal are sent until newline is entered.
While I can find an alternative way of doing this, I would like to know if this is the way readByteSync should or must work. If so, I’ll find an alternative way of doing what I want.
// Example program //
import 'dart:io';
void main () {
int iInput;
List<int> lCharCodes = [];
print(""); print("");
String sDefault = "abc";
stdout.write ("Enter data : $sDefault\b\b\b");
while (iInput != 10) { // wait for newline
iInput = stdin.readByteSync();
if (iInput == 8 && lCharCodes.length > 0) { // bs
lCharCodes.removeLast();
} else if (iInput > 31) { // ascii printable char
lCharCodes.add(iInput);
if (lCharCodes.length == 1)
stdout.write (" \b\b\b\b chars cleared"); // clear line
print ("\nlCharCodes length = ${lCharCodes.length}");
}
}
print ("\nData entered = ${new String.fromCharCodes(lCharCodes).trim()}");
}
Results on Command screen are :
c:\Users\Brian\dart-dev1\test\bin>dart testsync001.dart
Enter data : xxc
chars cleared
lCharCodes length = 1
lCharCodes length = 2
Data entered = xx
c:\Users\Brian\dart-dev1\test\bin>
I recently added stdin.readByteSync and readLineSync, to easier create small scrips reading the stdin. However, two things are still missing, for this to be feature-complete.
1) Line mode vs Raw mode. This is basically what you are asking for, a way to get a char as soon as it's printed.
2) Echo on/off. This mode is useful for e.g. typing in passwords, so you can disable the default echo of the characters.
I hope to be able to implement and land these features rather soon.
You can star this bug to track the development of it!
This is common behavior for consoles. Try to flush the output with stdout.flush().
Edit: my mistake. I looked at a very old revision (dartlang-test). The current API does not provide any means to flush stdout. Feel free to file a bug.

Triggering OK from the call back on GetParm

I am using the IUP.GetParm dialog to do a search and replace prompt.
The dialog supports 3 buttons, the first two OK and Cancel close the prompt and return to the main program flow.
The third button can be tracked in the parm_action function, what I want to do is use the third button to skip the item and close the dialog, but I can't work out if this is possible.
I have asked this on the IUP mailing list but have not yet had a response.
function param_action(dialog,index)
if index == -4 then
bSkip = true
return 1
end
end
bSkip = false
bConfirm,strFromString,strToString,bSkip =
iup.GetParam("Search and Replace",
param_action,
fhGetTag(ptrRecord)..'-'..fhGetTag(ptr)..
' '..fhGetDisplayText(ptrRecord).." %t\n"..
"Replace: "..strBoxType.."\n"..
"With: "..strBoxType.."\n"..
"btn: %u[Ok,Cancel,Skip] \n"
, strFromString,strToString)
if bConfirm and not(bSkip) then
-- replace string
end
To make this function currently you have to press the Skip button and then the Ok button.
Just re-posting the answer from the IUP mailing list here:
Inside the call-back, when the 3rd button is pressed, set the dialog
attribute "status" to "1" and call the function iup.ExitLoop().

Resources