diff --git a/kitty/boss.py b/kitty/boss.py index 27e24c5d1..7475f1796 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -264,6 +264,10 @@ class Boss: cwd_from = w.child.pid_for_cwd if w is not None else None self._new_os_window(args, cwd_from) + def new_os_window_with_wd(self, wd): + special_window = SpecialWindow(None, cwd=wd) + self._new_os_window(special_window) + def add_child(self, window): self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen) self.window_id_map[window.id] = window @@ -937,6 +941,10 @@ class Boss: cwd_from = w.child.pid_for_cwd if w is not None else None self._create_tab(args, cwd_from=cwd_from) + def new_tab_with_wd(self, wd): + special_window = SpecialWindow(None, cwd=wd) + self._new_tab(special_window) + def _new_window(self, args, cwd_from=None): tab = self.active_tab if tab is not None: diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 02b14e97a..01a94525a 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -869,6 +869,7 @@ process_pending_closes(ChildMonitor *self) { // via the mouse causes a crash because of the way autorelease pools work in // glfw/cocoa. So we use a flag instead. static unsigned int cocoa_pending_actions = 0; +static char *cocoa_pending_actions_wd = NULL; void set_cocoa_pending_action(CocoaPendingAction action) { @@ -877,6 +878,12 @@ set_cocoa_pending_action(CocoaPendingAction action) { // Unjam it so the pending action is processed right now. unjam_event_loop(); } + +void +set_cocoa_pending_action_with_wd(CocoaPendingAction action, const char *wd) { + cocoa_pending_actions_wd = strdup(wd); + set_cocoa_pending_action(action); +} #endif static PyObject* @@ -893,7 +900,11 @@ main_loop(ChildMonitor *self, PyObject *a UNUSED) { if (cocoa_pending_actions) { if (cocoa_pending_actions & PREFERENCES_WINDOW) { call_boss(edit_config_file, NULL); } if (cocoa_pending_actions & NEW_OS_WINDOW) { call_boss(new_os_window, NULL); } + if (cocoa_pending_actions & NEW_OS_WINDOW_WITH_WD) { call_boss(new_os_window_with_wd, "s", cocoa_pending_actions_wd); } + if (cocoa_pending_actions & NEW_TAB_WITH_WD) { call_boss(new_tab_with_wd, "s", cocoa_pending_actions_wd); } cocoa_pending_actions = 0; + free(cocoa_pending_actions_wd); + cocoa_pending_actions_wd = NULL; } #endif parse_input(self); diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index df9b7ef76..f6fc78a44 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -187,6 +187,38 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) { Py_RETURN_NONE; } +@interface ServiceProvider : NSObject +@end + +@implementation ServiceProvider + +- (void)openTab:(NSPasteboard*)pasteboard + userData:(NSString *) UNUSED userData error:(NSError **) UNUSED error { + [self openFilesFromPasteboard:pasteboard type:NEW_TAB_WITH_WD]; +} + +- (void)openOSWindow:(NSPasteboard*)pasteboard + userData:(NSString *) UNUSED userData error:(NSError **) UNUSED error { + [self openFilesFromPasteboard:pasteboard type:NEW_OS_WINDOW_WITH_WD]; +} + +- (void)openFilesFromPasteboard:(NSPasteboard *)pasteboard type:(int)type { + NSDictionary *options = [NSDictionary dictionaryWithObject:@YES forKey:NSPasteboardURLReadingFileURLsOnlyKey]; + NSArray *filePathArray = [pasteboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:options]; + for (NSURL *url in filePathArray) { + NSString *path = [url path]; + BOOL isDirectory = NO; + if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]) { + if (!isDirectory) { + path = [path stringByDeletingLastPathComponent]; + } + set_cocoa_pending_action_with_wd(type, [path UTF8String]); + } + } +} + +@end + // global menu {{{ void cocoa_create_global_menu(void) { @@ -278,6 +310,9 @@ cocoa_create_global_menu(void) { @selector(applicationDockMenu:), (IMP)get_dock_menu, "@@:@"); + + + [NSApp setServicesProvider:[[[ServiceProvider alloc] init] autorelease]]; } void diff --git a/kitty/state.h b/kitty/state.h index 062b91919..7c2b5d1de 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -206,9 +206,13 @@ void send_prerendered_sprites_for_window(OSWindow *w); #ifdef __APPLE__ void get_cocoa_key_equivalent(int, int, unsigned short*, int*); typedef enum { - PREFERENCES_WINDOW = 1, NEW_OS_WINDOW = 2 + PREFERENCES_WINDOW = 1, + NEW_OS_WINDOW = 2, + NEW_OS_WINDOW_WITH_WD = 4, + NEW_TAB_WITH_WD = 8 } CocoaPendingAction; void set_cocoa_pending_action(CocoaPendingAction action); +void set_cocoa_pending_action_with_wd(CocoaPendingAction action, const char *wd); bool application_quit_requested(); void request_application_quit(); #endif diff --git a/setup.py b/setup.py index 4cb054e98..f8643fced 100755 --- a/setup.py +++ b/setup.py @@ -724,6 +724,20 @@ Categories=System;TerminalEmulator; NSSupportsAutomaticGraphicsSwitching=True, LSApplicationCategoryType='public.app-category.utilities', LSEnvironment={'KITTY_LAUNCHED_BY_LAUNCH_SERVICES': '1'}, + NSServices=[ + { + 'NSMenuItem': {'default': 'New ' + appname + ' Tab Here'}, + 'NSMessage': 'openTab', + 'NSRequiredContext': {'NSTextContent': 'FilePath'}, + 'NSSendTypes': ['NSFilenamesPboardType', 'public.plain-text'], + }, + { + 'NSMenuItem': {'default': 'New ' + appname + ' Window Here'}, + 'NSMessage': 'openOSWindow', + 'NSRequiredContext': {'NSTextContent': 'FilePath'}, + 'NSSendTypes': ['NSFilenamesPboardType', 'public.plain-text'], + }, + ], ) with open('Info.plist', 'wb') as fp: plistlib.dump(pl, fp)