diff --git a/Hydra.xcodeproj/project.pbxproj b/Hydra.xcodeproj/project.pbxproj index 18d89231..0e965e05 100644 --- a/Hydra.xcodeproj/project.pbxproj +++ b/Hydra.xcodeproj/project.pbxproj @@ -99,6 +99,7 @@ 948CAA0B19561D7300A27D9D /* timer.lua in Resources */ = {isa = PBXBuildFile; fileRef = 948CAA0A19561D7300A27D9D /* timer.lua */; }; 94A1537C194D77A400F29F8F /* window.lua in Resources */ = {isa = PBXBuildFile; fileRef = 94A1537B194D77A400F29F8F /* window.lua */; }; 94ABF0901980B5E900FA68E9 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94ABF08E1980B41300FA68E9 /* Security.framework */; }; + 94B6A9A11984974F00158B7B /* screen_watcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 94B6A9A01984974F00158B7B /* screen_watcher.m */; }; 94BDB49C19094B7A00BD0553 /* application.m in Sources */ = {isa = PBXBuildFile; fileRef = 94BDB49B19094B7A00BD0553 /* application.m */; }; 94BEAA58197EFD8300C4B1CF /* spaces.m in Sources */ = {isa = PBXBuildFile; fileRef = 94BEAA57197EFD8300C4B1CF /* spaces.m */; }; 94C3693E194DD93600EE3A2D /* window.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C3693D194DD93600EE3A2D /* window.m */; }; @@ -248,6 +249,7 @@ 948CAA0A19561D7300A27D9D /* timer.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = timer.lua; sourceTree = ""; }; 94A1537B194D77A400F29F8F /* window.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = window.lua; sourceTree = ""; }; 94ABF08E1980B41300FA68E9 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 94B6A9A01984974F00158B7B /* screen_watcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = screen_watcher.m; sourceTree = ""; }; 94BDB49B19094B7A00BD0553 /* application.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = application.m; sourceTree = ""; }; 94BEAA57197EFD8300C4B1CF /* spaces.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = spaces.m; sourceTree = ""; }; 94C3693D194DD93600EE3A2D /* window.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = window.m; sourceTree = ""; }; @@ -493,6 +495,7 @@ 946AF2D51976B87A00A22EBF /* pasteboard.m */, 945212A1194D69E700217584 /* pathwatcher.m */, 94E8E79F194E0AB10064CF6C /* screen.m */, + 94B6A9A01984974F00158B7B /* screen_watcher.m */, 94BEAA57197EFD8300C4B1CF /* spaces.m */, 948CA9FE1955AECC00A27D9D /* textgrid.m */, 944419C0194F4A51000A55A7 /* timer.m */, @@ -636,6 +639,7 @@ 943F212F195C527300066DCB /* hydra_settings.m in Sources */, 9445CA2219083251002568BB /* main.m in Sources */, 9445CA86190833C1002568BB /* ldebug.c in Sources */, + 94B6A9A11984974F00158B7B /* screen_watcher.m in Sources */, 9445CA8C190833C1002568BB /* liolib.c in Sources */, 9445CA93190833C1002568BB /* loslib.c in Sources */, 9452129C194D5B8F00217584 /* hydra_autolaunch.m in Sources */, diff --git a/Hydra/API/screen_watcher.h b/Hydra/API/screen_watcher.h new file mode 100644 index 00000000..d3f6300e --- /dev/null +++ b/Hydra/API/screen_watcher.h @@ -0,0 +1,13 @@ +// +// screen_watcher.h +// Hydra +// +// Created by Steven on 7/26/14. +// Copyright (c) 2014 Steven. All rights reserved. +// + +#import + +@interface screen_watcher : NSObject + +@end diff --git a/Hydra/API/screen_watcher.m b/Hydra/API/screen_watcher.m new file mode 100644 index 00000000..dfc17f1a --- /dev/null +++ b/Hydra/API/screen_watcher.m @@ -0,0 +1,109 @@ +#import "helpers.h" + +@interface HydraScreenWatcher : NSObject +@property lua_State* L; +@property int fn; +@end + +@implementation HydraScreenWatcher +- (void) screensChanged:(id)bla { + lua_State* L = self.L; + lua_rawgeti(L, LUA_REGISTRYINDEX, self.fn); + if (lua_pcall(L, 0, 0, 0)) + hydra_handle_error(L); +} +@end + + +typedef struct _screenwatcher_t { + bool running; + int fn; + void* obj; +} screenwatcher_t; + +/// screen.watcher.new(fn) -> watcher +/// Creates a new screen-watcher that can be started; fn will be called when your screen layout changes in any way, whether by adding/removing/moving monitors or like whatever. +static int screen_watcher_new(lua_State* L) { + luaL_checktype(L, 1, LUA_TFUNCTION); + + screenwatcher_t* screenwatcher = lua_newuserdata(L, sizeof(screenwatcher_t)); + memset(screenwatcher, 0, sizeof(screenwatcher_t)); + + lua_pushvalue(L, 1); + screenwatcher->fn = luaL_ref(L, LUA_REGISTRYINDEX); + + HydraScreenWatcher* object = [[HydraScreenWatcher alloc] init]; + object.L = L; + object.fn = screenwatcher->fn; + screenwatcher->obj = (__bridge_retained void*)object; + + luaL_getmetatable(L, "screen_watcher"); + lua_setmetatable(L, -2); + + return 1; +} + +static int screen_watcher_start(lua_State* L) { + screenwatcher_t* screenwatcher = luaL_checkudata(L, 1, "screen_watcher"); + + if (screenwatcher->running) return 0; + screenwatcher->running = true; + + [[NSNotificationCenter defaultCenter] addObserver:(__bridge id)screenwatcher->obj + selector:@selector(screensChanged:) + name:NSApplicationDidChangeScreenParametersNotification + object:nil]; + + return 0; +} + +static int screen_watcher_stop(lua_State* L) { + screenwatcher_t* screenwatcher = luaL_checkudata(L, 1, "screen_watcher"); + + if (!screenwatcher->running) return 0; + screenwatcher->running = false; + + [[NSNotificationCenter defaultCenter] removeObserver:(__bridge id)screenwatcher->obj]; + + return 0; +} + +static int screen_watcher_stopall(lua_State* L) { + lua_getglobal(L, "screen"); + lua_getfield(L, -1, "watcher"); + lua_getfield(L, -1, "stop"); + hydra_remove_all_handlers(L, "screen_watcher"); + return 0; +} + +static int screen_watcher_gc(lua_State* L) { + screenwatcher_t* screenwatcher = luaL_checkudata(L, 1, "screen_watcher"); + + luaL_unref(L, LUA_REGISTRYINDEX, screenwatcher->fn); + + HydraScreenWatcher* object = (__bridge_transfer id)screenwatcher->obj; + object = nil; + + return 0; +} + +static luaL_Reg screen_watcherlib[] = { + {"new", screen_watcher_new}, + {"start", screen_watcher_start}, + {"stop", screen_watcher_stop}, + {"stopall", screen_watcher_stopall}, + {"__gc", screen_watcher_gc}, + {} +}; + +int luaopen_screen_watcher(lua_State* L) { + luaL_newlib(L, screen_watcherlib); + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, "screen_watcher"); + + return 1; +} diff --git a/Hydra/Bootstrapping/main.m b/Hydra/Bootstrapping/main.m index b9e31a93..7ed9d771 100644 --- a/Hydra/Bootstrapping/main.m +++ b/Hydra/Bootstrapping/main.m @@ -31,6 +31,7 @@ int main(int argc, const char * argv[]) { int luaopen_pasteboard(lua_State* L); int luaopen_pathwatcher(lua_State* L); int luaopen_screen(lua_State* L); +int luaopen_screen_watcher(lua_State* L); int luaopen_spaces(lua_State* L); int luaopen_textgrid(lua_State* L); int luaopen_timer(lua_State* L); @@ -79,7 +80,9 @@ @implementation HydraAppDelegate {}}}, {"pasteboard", luaopen_pasteboard}, {"pathwatcher", luaopen_pathwatcher}, - {"screen", luaopen_screen}, + {"screen", luaopen_screen, (hydralib[]){ + {"watcher", luaopen_screen_watcher}, + {}}}, {"spaces", luaopen_spaces}, {"textgrid", luaopen_textgrid}, {"timer", luaopen_timer},