Closed spadino closed 7 years ago
I noticed that if I click on the titlebar after the window switch, it correctly sticky to the space. So, I need to insert a click on the title bar after the window movement, but I'm not sure how to do this...
Check this thread for solutions: https://github.com/Hammerspoon/hammerspoon/issues/581
My working version: https://github.com/szymonkaliski/Dotfiles/blob/5ab45f6392a0c1a5354f14a9871589fea2e67d26/Dotfiles/hammerspoon/ext/window.lua#L197-L251
Note that szymonkaliski's solution uses asmagill's spaces module which you will need to put in your ~/.hammerspoon/
as detailed on the readme before using.
Spaces are not officially supported because there is no public API, but asmagill's module seems to work reliably.
Oh yeah, forgot to mention that, sorry :)
Many thanks for pointing me to the right direction. Anyway, it's really difficult to understand how can I put it in a simple start setup. By now, I just need that feature. Can someone kindly instruct me how to implement such feature? The Dotfiles repo are very difficult to understand...
Szymon Kaliski wrote:
Oh yeah, forgot to mention that, sorry :)
— Reply to this email directly or view it on GitHub https://github.com/Hammerspoon/hammerspoon/issues/823#issuecomment-192958673.
Andrea Spada :: Liuteria d'Autore www.andreaspada.com
szymonkaliski's hammerspoon config is a bit more sophisticated than most, but full of great stuff and worth taking the time to decipher.
First download the latest release of asmagill's spaces module. Extract it and move it to your ~/.hammerspoon/
wholesale (so that ~/.hammerspoon/hs/_asm/undocumented/spaces/internal.so
exists).
Anyway, to rip out his screen-switching setup as a single-file (hope he doesn't mind 😬), put this in your init.lua
(you can just comment out the os.execute(template([[ /usr/local/bin/cliclick...
line in focusScreen
or simply brew install cliclick
first):
-- require traverses directories in your ~/.hammerspoon folder, with directory levels separated by dots
local spaces = require('hs._asm.undocumented.spaces')
local cache = {
mousePosition = nil
}
-- grabs screen with active window, unless it's Finder's desktop
-- then we use mouse position
local function activeScreen()
local mousePoint = hs.geometry.point(hs.mouse.getAbsolutePosition())
local activeWindow = hs.window.focusedWindow()
if activeWindow and activeWindow:role() ~= 'AXScrollArea' then
return activeWindow:screen()
else
return hs.fnutils.find(hs.screen.allScreens(), function(screen)
return mousePoint:inside(screen:frame())
end)
end
end
local function focusScreen(screen)
local frame = screen:frame()
-- if mouse is already on the given screen we can safely return
if hs.geometry(hs.mouse.getAbsolutePosition()):inside(frame) then return false end
-- "hide" cursor in the lower right side of screen
-- it's invisible while we are changing spaces
local mousePosition = {
x = frame.x + frame.w - 1,
y = frame.y + frame.h - 1
}
-- hs.mouse.setAbsolutePosition doesn't work for gaining proper screen focus
-- moving the mouse pointer with cliclick (available on homebrew) works
os.execute(template([[ /usr/local/bin/cliclick m:={X},{Y} ]], { X = mousePosition.x, Y = mousePosition.y }))
hs.timer.usleep(1000)
return true
end
local function activeSpaceIndex(screenSpaces)
return hs.fnutils.indexOf(screenSpaces, spaces.activeSpace()) or 1
end
local function screenSpaces(currentScreen)
currentScreen = currentScreen or activeScreen()
return spaces.layout()[currentScreen:spacesUUID()]
end
local function spaceInDirection(direction)
local screenSpaces = screenSpaces()
local activeIdx = activeSpaceIndex(screenSpaces)
local targetIdx = direction == 'west' and activeIdx - 1 or activeIdx + 1
return screenSpaces[targetIdx]
end
local function moveToSpaceInDirection(win, direction)
local clickPoint = win:zoomButtonRect()
local sleepTime = 1000
local targetSpace = spaceInDirection(direction)
-- check if all conditions are ok to move the window
local shouldMoveWindow = hs.fnutils.every({
clickPoint ~= nil,
targetSpace ~= nil,
not cache.movingWindowToSpace
}, function(test) return test end)
if not shouldMoveWindow then return end
cache.movingWindowToSpace = true
cache.mousePosition = cache.mousePosition or hs.mouse.getAbsolutePosition()
clickPoint.x = clickPoint.x + clickPoint.w + 5
clickPoint.y = clickPoint.y + clickPoint.h / 2
-- fix for Chrome UI
if win:application():title() == 'Google Chrome' then
clickPoint.y = clickPoint.y - clickPoint.h
end
-- focus screen before switching window
focusScreen(win:screen())
hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseDown, clickPoint):post()
hs.timer.usleep(sleepTime)
hs.eventtap.keyStroke({ 'ctrl' }, direction == 'east' and 'right' or 'left')
hs.timer.waitUntil(
function()
return spaces.activeSpace() == targetSpace
end,
function()
hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseUp, clickPoint):post()
-- resetting mouse after small timeout is needed for focusing screen to work properly
hs.mouse.setAbsolutePosition(cache.mousePosition)
cache.mousePosition = nil
-- reset cache
cache.movingWindowToSpace = false
end,
0.01 -- check every 1/100 of a second
)
end
hs.fnutils.each({
{ key = 'home', direction = 'west' },
{ key = 'end', direction = 'east' }
}, function(object)
hs.hotkey.bind({"cmd", "option"}, object.key, nil, function() moveToSpaceInDirection(hs.window.frontmostWindow(), object.direction) end)
end)
It's definitely worth breaking big functionality like this into separate files. You can save the above as something like switchspaces.lua
and require 'switchspaces'
in your init.lua
.
Wow, thank's a lot for your help! I did as you wrote, and when reloading the config the console display this:
-- Lazy extension loading enabled -- Loading ~/.hammerspoon/init.lua -- Loading extension: fnutils -- Loading extension: hotkey 19:55:04 hotkey: Enabled hotkey ⌘⌥HOME hotkey: Enabled hotkey ⌘⌥END -- Done. -- Loading extension: window -- Loading extension: uielement -- Loading extension: geometry -- Loading extension: mouse
However, if I do ⌘⌥HOME in a window, it displays that: *\ ERROR: hs.hotkey callback error: /Users/andrea/.hammerspoon/init.lua:50: attempt to call a nil value (field 'layout') stack traceback: /Users/andrea/.hammerspoon/init.lua:50: in upvalue 'screenSpaces' /Users/andrea/.hammerspoon/init.lua:54: in upvalue 'spaceInDirection' /Users/andrea/.hammerspoon/init.lua:64: in upvalue 'moveToSpaceInDirection' /Users/andrea/.hammerspoon/init.lua:118: in function </Users/andrea/.hammerspoon/init.lua:118>
I do not know enough Hammerspoon to understand what is wrong here...
Michael B wrote:
szymonkaliski's hammerspoon config is a bit more sophisticated than most, but full of great stuff and worth taking the time to decipher.
First download the latest release of asmagill's spaces module https://github.com/asmagill/hs._asm.undocumented.spaces/releases. Extract it and move it to your |~/.hammerspoon/| wholesale (so that |~/.hammerspoon/hs/_asm/undocumented/spaces/internal.so| exists).
Anyway, to rip out his screen-switching setup as a single-file (hope he doesn't mind 😬), put this in your |init.lua| (you can just comment out the |os.execute(template([[ /usr/local/bin/cliclick...| line in |focusScreen| or simply |brew install cliclick| first):
-- require traverses directories in your ~/.hammerspoon folder, with directory levels separated by dots local spaces= require('hs._asm.undocumented.spaces')
local cache= { mousePosition= nil }
-- grabs screen with active window, unless it's Finder's desktop -- then we use mouse position local function activeScreen() local mousePoint= hs.geometry.point(hs.mouse.getAbsolutePosition()) local activeWindow= hs.window.focusedWindow()
if activeWindowand activeWindow:role()~= 'AXScrollArea' then return activeWindow:screen() else return hs.fnutils.find(hs.screen.allScreens(),function(screen) return mousePoint:inside(screen:frame()) end) end end
local function focusScreen(screen) local frame= screen:frame()
-- if mouse is already on the given screen we can safely return if hs.geometry(hs.mouse.getAbsolutePosition()):inside(frame)then return false end
-- "hide" cursor in the lower right side of screen -- it's invisible while we are changing spaces local mousePosition= { x= frame.x + frame.w - 1, y= frame.y + frame.h - 1 }
-- hs.mouse.setAbsolutePosition doesn't work for gaining proper screen focus -- moving the mouse pointer with cliclick (available on homebrew) works os.execute(template([[ /usr/local/bin/cliclick m:={X},{Y}]], { X= mousePosition.x, Y= mousePosition.y })) hs.timer.usleep(1000)
return true end
local function activeSpaceIndex(screenSpaces) return hs.fnutils.indexOf(screenSpaces, spaces.activeSpace())or 1 end
local function screenSpaces(currentScreen) currentScreen= currentScreenor activeScreen() return spaces.layout()[currentScreen:spacesUUID()] end
local function spaceInDirection(direction) local screenSpaces= screenSpaces() local activeIdx= activeSpaceIndex(screenSpaces) local targetIdx= direction== 'west' and activeIdx- 1 or activeIdx+ 1
return screenSpaces[targetIdx] end
local function moveToSpaceInDirection(win, direction) local clickPoint= win:zoomButtonRect() local sleepTime= 1000 local targetSpace= spaceInDirection(direction)
-- check if all conditions are ok to move the window local shouldMoveWindow= hs.fnutils.every({ clickPoint~= nil, targetSpace~= nil, not cache.movingWindowToSpace },function(test)return testend)
if not shouldMoveWindowthen return end
cache.movingWindowToSpace = true
cache.mousePosition = cache.mousePosition or hs.mouse.getAbsolutePosition()
clickPoint.x = clickPoint.x + clickPoint.w + 5 clickPoint.y = clickPoint.y + clickPoint.h / 2
-- fix for Chrome UI if win:application():title()== 'Google Chrome' then clickPoint.y = clickPoint.y - clickPoint.h end
-- focus screen before switching window focusScreen(win:screen())
hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseDown, clickPoint):post() hs.timer.usleep(sleepTime)
hs.eventtap.keyStroke({'ctrl' }, direction== 'east' and 'right' or 'left')
hs.timer.waitUntil( function() return spaces.activeSpace()== targetSpace end, function() hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseUp, clickPoint):post()
-- resetting mouse after small timeout is needed for focusing screen to work properly hs.mouse.setAbsolutePosition(cache.mousePosition) cache.mousePosition = nil -- reset cache cache.movingWindowToSpace = false end, 0.01 -- check every 1/100 of a second
) end
hs.fnutils.each({ { key= 'home', direction= 'west' }, { key= 'end', direction= 'east' } },function(object) hs.hotkey.bind({"cmd","option"}, object.key,nil,function()moveToSpaceInDirection(hs.window.frontmostWindow(), object.direction)end) end)
It's definitely worth breaking big functionality like this into separate files. You can save the above as something like |switchspaces.lua| and |require 'switchspaces'| in your |init.lua|.
— Reply to this email directly or view it on GitHub https://github.com/Hammerspoon/hammerspoon/issues/823#issuecomment-193008104.
Andrea Spada :: Liuteria d'Autore www.andreaspada.com
Are you using the latest version of the spaces module?
Yes!
Michael B wrote:
Are you using the latest version of the spaces module?
— Reply to this email directly or view it on GitHub https://github.com/Hammerspoon/hammerspoon/issues/823#issuecomment-193014766.
Andrea Spada :: Liuteria d'Autore www.andreaspada.com
Well, it must not latest because your error says that spaces.layout
is nil
, and the layout function was not present in the earliest version.
I'll check, but I just downloaded it from Github. I'll try to compile from source, synched with master, than I'll let you know. May thanks for your help, really! A.
Michael B wrote:
Well, it must not latest because your error says that |spaces.layout| is |nil|, and the layout function was not present in the earliest version.
— Reply to this email directly or view it on GitHub https://github.com/Hammerspoon/hammerspoon/issues/823#issuecomment-193038459.
Andrea Spada :: Liuteria d'Autore www.andreaspada.com
@andreaspada you actually don't need to call focusScreen
at all, it's just something I've added for myself, to make switching spaces on multiple displays switch the space with active window, and not where the mouse actually is, but you might not want that, so it could be even more simplified. And I compile spaces addon from source, so you might want to do that as well. Good luck! And thanks @heptal for jumping in while I was sleeping :)
So, compiled from source - synched with master branch, installed and config reload. On OSX 10.11.3. Everything ok. Xcode updated - gui and cli. Brew updated. Not working yet. Here it is the output of the console...
-- Lazy extension loading enabled -- Loading ~/.hammerspoon/init.lua -- Loading extension: uielement -- Loading extension: fnutils -- Loading extension: hotkey 09:33:19 hotkey: Enabled hotkey ⌘⌃LEFT hotkey: Enabled hotkey ⌘⌃RIGHT -- Done.
Than, after try to move the windows:
-- Loading extension: window -- Loading extension: geometry -- Loading extension: mouse *\ ERROR: hs.hotkey callback error: ...andrea/.hammerspoon/hs/_asm/undocumented/spaces/init.lua:419: attempt to call a nil value (field 'details') stack traceback: ...andrea/.hammerspoon/hs/_asm/undocumented/spaces/init.lua:419: in function 'hs._asm.undocumented.spaces.layout' /Users/andrea/.hammerspoon/init.lua:50: in upvalue 'screenSpaces' /Users/andrea/.hammerspoon/init.lua:54: in upvalue 'spaceInDirection' /Users/andrea/.hammerspoon/init.lua:64: in upvalue 'moveToSpaceInDirection' /Users/andrea/.hammerspoon/init.lua:118: in function </Users/andrea/.hammerspoon/init.lua:118>
Here it is my local setup. https://github.com/andreaspada/DOThammerspoon
Hope I understand what is wrong with it...
Cheers, Andrea
Szymon Kaliski wrote:
@andreaspada https://github.com/andreaspada you actually don't need to call |focusScreen| at all, it's just something I've added to myself, to make switching spaces on multiple displays switch the space with active window, and not where the mouse actually is, but you might not want that, so it could be even more simplified. And I compile spaces addon from source, so you might want to do that as well. Good luck! And thanks @heptal https://github.com/heptal for jumping in while I was sleeping :)
— Reply to this email directly or view it on GitHub https://github.com/Hammerspoon/hammerspoon/issues/823#issuecomment-193163742.
Andrea Spada :: Liuteria d'Autore www.andreaspada.com
I'm going to close this because I'm gardening the bugs and this has been open for ages with no further discussion, feel free to re-open it if there are specific things you want to do :)
I'm using that scripts to move my windows to the space at left or right:
Although, the window is not fixed on the switched space. If I move the window, than moving back on the previous space, the window follow the space switching. Anyone know how can I prevent it? Also, anyone have a clue to just send a window to the space without actually switching to that space?