aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStéphane Marks2026-02-04 18:20:55 -0500
committerMichael Albinus2026-02-11 09:52:38 +0100
commit0bee754a7204f911f934d750f6f1870c929ccdb0 (patch)
tree8d2d3deb7affa24d579af9fbc2546764bdb1200b /src
parentf13ab20f0456f311bc5e1374db2e638bed17f8e9 (diff)
downloademacs-0bee754a7204f911f934d750f6f1870c929ccdb0.tar.gz
emacs-0bee754a7204f911f934d750f6f1870c929ccdb0.zip
system-sleep sleep blocker and sleep/wake event package (bug#80348)
This package provides platform-neutral interfaces to block your system from entering idle sleep and a hook to process pre-sleep and post-wake events. Implementations are for D-Bus on GNU/Linux, macOS/GNUstep, and MS-Windows. * lisp/system-sleep.el: New package. * src/fns.c: Qpre_sleep, Qpost_wake: New DEFSYM. * src/nsfns.m (Fns_block_system_sleep, Fns_unblock_system_sleep) (syms_of_nsfns): New functions. * src/nsterm.m (applicationDidFinishLaunching): Subscribe to pre-sleep and post-wake notifications. (systemWillSleep, systemDidWake): New function. * src/w32fns.c (Fw32_block_system_sleep) (Fw32_unblock_system_sleep, Fw32_system_sleep_block_count) (sleep_notification_callback) (w32_register_for_sleep_notifications): New function. (syms_of_w32fns): Sw32_unblock_system_sleep Sw32_block_system_sleep Sw32_system_sleep_block_count: New defsubr. * src/w32term.h (Fw32_block_system_sleep): New extern. * src/w32term.c (w32_initialize): Call w32_register_for_sleep_notifications. * doc/lispref/os.texi: Document the system-sleep package. * doc/lispref/commands.texi: Update sleep-event special documentation. * etc/NEWS: Announce the new package.
Diffstat (limited to 'src')
-rw-r--r--src/fns.c4
-rw-r--r--src/nsfns.m84
-rw-r--r--src/nsterm.m48
-rw-r--r--src/w32fns.c145
-rw-r--r--src/w32term.c1
-rw-r--r--src/w32term.h1
6 files changed, 274 insertions, 9 deletions
diff --git a/src/fns.c b/src/fns.c
index 5c30d950cff..c29f9fa8cd1 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -6891,4 +6891,8 @@ For best results this should end in a space. */);
6891 DEFSYM (QCin_place, ":in-place"); 6891 DEFSYM (QCin_place, ":in-place");
6892 DEFSYM (QCreverse, ":reverse"); 6892 DEFSYM (QCreverse, ":reverse");
6893 DEFSYM (Qvaluelt, "value<"); 6893 DEFSYM (Qvaluelt, "value<");
6894
6895 /* sleep-event states. */
6896 DEFSYM (Qpre_sleep, "pre-sleep");
6897 DEFSYM (Qpost_wake, "post-wake");
6894} 6898}
diff --git a/src/nsfns.m b/src/nsfns.m
index cf685630ab7..3d3d5ec1bde 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -3805,6 +3805,88 @@ If PROGRESS is nil, remove the progress indicator. */)
3805 return Qnil; 3805 return Qnil;
3806} 3806}
3807 3807
3808/* A unique integer sleep block id and a hash map of its id to opaque
3809 NSObject sleep block activity tokens. */
3810static unsigned int sleep_block_id = 0;
3811static NSMutableDictionary *sleep_block_map = NULL;
3812
3813DEFUN ("ns-block-system-sleep",
3814 Fns_block_system_sleep,
3815 Sns_block_system_sleep,
3816 2, 2, 0,
3817 doc: /* Block system idle sleep.
3818WHY is a string reason for the block.
3819If ALLOW-DISPLAY-SLEEP is non-nil, block the screen from sleeping.
3820Return a token to unblock this block using `ns-unblock-system-sleep',
3821or nil if the block fails. */)
3822 (Lisp_Object why, Lisp_Object allow_display_sleep)
3823{
3824 block_input ();
3825
3826 NSString *reason = @"Emacs";
3827 if (!NILP (why))
3828 {
3829 CHECK_STRING (why);
3830 reason = [NSString stringWithLispString: why];
3831 }
3832
3833 unsigned long activity_options =
3834 NSActivityUserInitiated | NSActivityIdleSystemSleepDisabled;
3835 if (NILP (allow_display_sleep))
3836 activity_options |= NSActivityIdleDisplaySleepDisabled;
3837
3838 NSProcessInfo *processInfo = [NSProcessInfo processInfo];
3839 NSObject *activity_id = nil;
3840 if ([processInfo respondsToSelector:@selector(beginActivityWithOptions:reason:)])
3841 activity_id = [[NSProcessInfo processInfo]
3842 beginActivityWithOptions: activity_options
3843 reason: reason];
3844 unblock_input ();
3845
3846 if (!sleep_block_map)
3847 sleep_block_map = [[NSMutableDictionary alloc] initWithCapacity: 25];
3848
3849 if (activity_id)
3850 {
3851 [sleep_block_map setObject: activity_id
3852 forKey: [NSNumber numberWithInt: ++sleep_block_id]];
3853 return make_fixnum (sleep_block_id);
3854 }
3855 else
3856 return Qnil;
3857}
3858
3859DEFUN ("ns-unblock-system-sleep",
3860 Fns_unblock_system_sleep,
3861 Sns_unblock_system_sleep,
3862 1, 1, 0,
3863 doc: /* Unblock system idle sleep.
3864TOKEN is an object returned by `ns-block-system-sleep'.
3865Return non-nil if the TOKEN block was unblocked. */)
3866 (Lisp_Object token)
3867{
3868 block_input ();
3869 Lisp_Object res = Qnil;
3870 CHECK_FIXNAT (token);
3871 if (sleep_block_map)
3872 {
3873 NSNumber *key = [NSNumber numberWithInt: XFIXNAT (token)];
3874 NSObject *activity_id = [sleep_block_map objectForKey: key];
3875 if (activity_id)
3876 {
3877 NSProcessInfo *processInfo = [NSProcessInfo processInfo];
3878 if ([processInfo respondsToSelector:@selector(endActivity:)])
3879 {
3880 [[NSProcessInfo processInfo] endActivity: activity_id];
3881 res = Qt;
3882 }
3883 [sleep_block_map removeObjectForKey: key];
3884 }
3885 }
3886 unblock_input ();
3887 return res;
3888}
3889
3808#ifdef NS_IMPL_COCOA 3890#ifdef NS_IMPL_COCOA
3809 3891
3810DEFUN ("ns-send-items", 3892DEFUN ("ns-send-items",
@@ -4091,6 +4173,8 @@ The default value is t. */);
4091 defsubr (&Sns_badge); 4173 defsubr (&Sns_badge);
4092 defsubr (&Sns_request_user_attention); 4174 defsubr (&Sns_request_user_attention);
4093 defsubr (&Sns_progress_indicator); 4175 defsubr (&Sns_progress_indicator);
4176 defsubr (&Sns_block_system_sleep);
4177 defsubr (&Sns_unblock_system_sleep);
4094#ifdef NS_IMPL_COCOA 4178#ifdef NS_IMPL_COCOA
4095 defsubr (&Sns_send_items); 4179 defsubr (&Sns_send_items);
4096#endif 4180#endif
diff --git a/src/nsterm.m b/src/nsterm.m
index c852b70be74..d0bbd1b4660 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -5838,15 +5838,6 @@ ns_term_init (Lisp_Object display_name)
5838 ns_pending_service_names = [[NSMutableArray alloc] init]; 5838 ns_pending_service_names = [[NSMutableArray alloc] init];
5839 ns_pending_service_args = [[NSMutableArray alloc] init]; 5839 ns_pending_service_args = [[NSMutableArray alloc] init];
5840 5840
5841#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
5842 /* Disable problematic event processing on macOS 26 (Tahoe) to avoid
5843 scrolling lag and input handling issues. These are undocumented
5844 options as of macOS 26.0. */
5845 [NSUserDefaults.standardUserDefaults
5846 registerDefaults:@{@"NSEventConcurrentProcessingEnabled" : @"NO",
5847 @"NSApplicationUpdateCycleEnabled" : @"NO"}];
5848#endif
5849
5850 /* Start app and create the main menu, window, view. 5841 /* Start app and create the main menu, window, view.
5851 Needs to be here because ns_initialize_display_info () uses AppKit classes. 5842 Needs to be here because ns_initialize_display_info () uses AppKit classes.
5852 The view will then ask the NSApp to stop and return to Emacs. */ 5843 The view will then ask the NSApp to stop and return to Emacs. */
@@ -6384,6 +6375,20 @@ ns_term_shutdown (int sig)
6384#endif 6375#endif
6385 6376
6386#ifdef NS_IMPL_COCOA 6377#ifdef NS_IMPL_COCOA
6378 /* Sleep event notification. */
6379 [[[NSWorkspace sharedWorkspace] notificationCenter]
6380 addObserver: self
6381 selector:@selector(systemWillSleep:)
6382 name: NSWorkspaceWillSleepNotification
6383 object: nil];
6384 [[[NSWorkspace sharedWorkspace] notificationCenter]
6385 addObserver: self
6386 selector: @selector(systemDidWake:)
6387 name: NSWorkspaceDidWakeNotification
6388 object: nil];
6389#endif
6390
6391#ifdef NS_IMPL_COCOA
6387 /* Some functions/methods in CoreFoundation/Foundation increase the 6392 /* Some functions/methods in CoreFoundation/Foundation increase the
6388 maximum number of open files for the process in their first call. 6393 maximum number of open files for the process in their first call.
6389 We make dummy calls to them and then reduce the resource limit 6394 We make dummy calls to them and then reduce the resource limit
@@ -6421,6 +6426,31 @@ ns_term_shutdown (int sig)
6421#endif 6426#endif
6422} 6427}
6423 6428
6429/* Sleep event notification. */
6430
6431- (void) systemWillSleep:(NSNotification *)notification
6432{
6433#ifdef NS_IMPL_COCOA
6434 NSTRACE ("[EmacsApp systemWillSleep:]");
6435 struct input_event ie;
6436 EVENT_INIT (ie);
6437 ie.kind = SLEEP_EVENT;
6438 ie.arg = list1 (Qpre_sleep);
6439 kbd_buffer_store_event (&ie);
6440#endif
6441}
6442
6443- (void) systemDidWake:(NSNotification *)notification
6444{
6445#ifdef NS_IMPL_COCOA
6446 NSTRACE ("[EmacsApp systemDidWake:]");
6447 struct input_event ie;
6448 EVENT_INIT (ie);
6449 ie.kind = SLEEP_EVENT;
6450 ie.arg = list1 (Qpost_wake);
6451 kbd_buffer_store_event (&ie);
6452#endif
6453}
6424 6454
6425/* Termination sequences: 6455/* Termination sequences:
6426 C-x C-c: 6456 C-x C-c:
diff --git a/src/w32fns.c b/src/w32fns.c
index b75bce8d1a2..3a32d046132 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -11326,6 +11326,136 @@ if the selected frame is not (yet) associated with a window handle */)
11326#endif /* WINDOWSNT */ 11326#endif /* WINDOWSNT */
11327 11327
11328/*********************************************************************** 11328/***********************************************************************
11329 System Sleep Support
11330 ***********************************************************************/
11331
11332typedef ULONG (WINAPI * SetThreadExecutionState_Proc)
11333 (IN ULONG);
11334static SetThreadExecutionState_Proc SetThreadExecutionState_fn = NULL;
11335
11336static unsigned int sleep_block_id = 0;
11337static unsigned int sleep_block_count = 0;
11338
11339DEFUN ("w32-block-system-sleep",
11340 Fw32_block_system_sleep,
11341 Sw32_block_system_sleep,
11342 1, 1, 0,
11343 doc: /* Block system idle sleep.
11344If ALLOW-DISPLAY-SLEEP is non-nil, block the screen from sleeping.
11345Return a token to unblock this block using `w32-unblock-system-sleep',
11346or nil if the block fails. */)
11347 (Lisp_Object allow_display_sleep)
11348{
11349 if (SetThreadExecutionState_fn == NULL)
11350 return Qnil;
11351
11352 /* ES_CONTINUOUS keeps the state until cleared. */
11353 EXECUTION_STATE new_state = ES_SYSTEM_REQUIRED | ES_CONTINUOUS;
11354 if (NILP (allow_display_sleep))
11355 new_state |= ES_DISPLAY_REQUIRED;
11356
11357 if (SetThreadExecutionState (new_state) == 0)
11358 return Qnil;
11359 else
11360 {
11361 /* One more block and next id. */
11362 ++sleep_block_count;
11363 ++sleep_block_id;
11364
11365 /* Synthesize a token. */
11366 return make_fixnum (sleep_block_id);
11367 }
11368}
11369
11370DEFUN ("w32-unblock-system-sleep",
11371 Fw32_unblock_system_sleep,
11372 Sw32_unblock_system_sleep,
11373 0, 0, 0,
11374 doc: /* Unblock system idle sleep.
11375Return non-nil if the TOKEN block was unblocked. */)
11376 (void)
11377{
11378 if (SetThreadExecutionState_fn == NULL)
11379 return Qnil;
11380
11381 /* No blocks to unblock. */
11382 if (sleep_block_count == 0)
11383 return Qnil;
11384
11385 /* One fewer block. */
11386 if (--sleep_block_count == 0
11387 && SetThreadExecutionState (ES_CONTINUOUS) == 0)
11388 return Qnil;
11389 else
11390 return Qt;
11391}
11392
11393DEFUN ("w32-system-sleep-block-count",
11394 Fw32_system_sleep_block_count,
11395 Sw32_system_sleep_block_count,
11396 0, 0, 0,
11397 doc: /* Return the w32 sleep block count. */)
11398 (void)
11399{
11400 return make_fixnum (sleep_block_count);
11401}
11402
11403typedef ULONG (CALLBACK *PMY_DEVICE_NOTIFY_CALLBACK_ROUTINE)
11404 (PVOID Context, ULONG Type, PVOID Setting);
11405
11406static ULONG ALIGN_STACK
11407sleep_notification_callback(PVOID _Context, ULONG Type, PVOID _Setting)
11408{
11409 struct input_event ie;
11410 EVENT_INIT (ie);
11411 ie.kind = SLEEP_EVENT;
11412
11413 switch (Type)
11414 {
11415 case PBT_APMRESUMEAUTOMATIC:
11416 /* Ignore this event. No user is present. */
11417 break;
11418 case PBT_APMSUSPEND:
11419 ie.arg = list1 (Qpre_sleep);
11420 kbd_buffer_store_event (&ie);
11421 break;
11422 case PBT_APMRESUMESUSPEND:
11423 ie.arg = list1 (Qpost_wake);
11424 kbd_buffer_store_event (&ie);
11425 break;
11426 }
11427 return 0;
11428}
11429
11430typedef HPOWERNOTIFY (WINAPI * RegisterSuspendResumeNotification_Proc)
11431 (IN HANDLE, IN DWORD);
11432static RegisterSuspendResumeNotification_Proc RegisterSuspendResumeNotification_fn = NULL;
11433
11434static HPOWERNOTIFY sleep_notification_handle = 0;
11435
11436typedef struct _MY_DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {
11437 PMY_DEVICE_NOTIFY_CALLBACK_ROUTINE Callback;
11438 PVOID Context;
11439} MY_DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS, *PMY_DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS;
11440
11441void
11442w32_register_for_sleep_notifications()
11443{
11444 /* PowerRegisterSuspendResumeNotification is not a user-space call so
11445 we use RegisterSuspendResumeNotification. */
11446 if (RegisterSuspendResumeNotification_fn)
11447 {
11448 MY_DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS params;
11449 params.Callback = sleep_notification_callback;
11450 params.Context = NULL;
11451
11452 /* DEVICE_NOTIFY_CALLBACK = 2 */
11453 sleep_notification_handle =
11454 RegisterSuspendResumeNotification_fn (&params, 2);
11455 }
11456}
11457
11458/***********************************************************************
11329 Initialization 11459 Initialization
11330 ***********************************************************************/ 11460 ***********************************************************************/
11331 11461
@@ -11834,6 +11964,10 @@ keys when IME input is received. */);
11834 defsubr (&Sw32_request_user_attention); 11964 defsubr (&Sw32_request_user_attention);
11835 DEFSYM (Qinformational, "informational"); 11965 DEFSYM (Qinformational, "informational");
11836 DEFSYM (Qcritical, "critical"); 11966 DEFSYM (Qcritical, "critical");
11967 /* System sleep support. */
11968 defsubr (&Sw32_unblock_system_sleep);
11969 defsubr (&Sw32_block_system_sleep);
11970 defsubr (&Sw32_system_sleep_block_count);
11837#endif 11971#endif
11838} 11972}
11839 11973
@@ -12094,6 +12228,7 @@ void
12094globals_of_w32fns (void) 12228globals_of_w32fns (void)
12095{ 12229{
12096 HMODULE user32_lib = GetModuleHandle ("user32.dll"); 12230 HMODULE user32_lib = GetModuleHandle ("user32.dll");
12231 HMODULE kernel32_lib = GetModuleHandle ("kernel32.dll");
12097 /* 12232 /*
12098 TrackMouseEvent not available in all versions of Windows, so must load 12233 TrackMouseEvent not available in all versions of Windows, so must load
12099 it dynamically. Do it once, here, instead of every time it is used. 12234 it dynamically. Do it once, here, instead of every time it is used.
@@ -12120,6 +12255,16 @@ globals_of_w32fns (void)
12120 RegisterTouchWindow_fn 12255 RegisterTouchWindow_fn
12121 = (RegisterTouchWindow_proc) get_proc_addr (user32_lib, 12256 = (RegisterTouchWindow_proc) get_proc_addr (user32_lib,
12122 "RegisterTouchWindow"); 12257 "RegisterTouchWindow");
12258 /* For system sleep support. */
12259 SetThreadExecutionState_fn
12260 = (SetThreadExecutionState_Proc)
12261 get_proc_addr (kernel32_lib,
12262 "SetThreadExecutionState");
12263 RegisterSuspendResumeNotification_fn
12264 = (RegisterSuspendResumeNotification_Proc)
12265 get_proc_addr (user32_lib,
12266 "RegisterSuspendResumeNotification");
12267
12123 SetGestureConfig_fn 12268 SetGestureConfig_fn
12124 = (SetGestureConfig_proc) get_proc_addr (user32_lib, 12269 = (SetGestureConfig_proc) get_proc_addr (user32_lib,
12125 "SetGestureConfig"); 12270 "SetGestureConfig");
diff --git a/src/w32term.c b/src/w32term.c
index 091a1fbd5f1..5b7d9c5f17d 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -8249,6 +8249,7 @@ w32_initialize (void)
8249 } 8249 }
8250 8250
8251 w32_get_mouse_wheel_vertical_delta (); 8251 w32_get_mouse_wheel_vertical_delta ();
8252 w32_register_for_sleep_notifications ();
8252} 8253}
8253 8254
8254void 8255void
diff --git a/src/w32term.h b/src/w32term.h
index 91db0b6e249..cb9d59371a4 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -274,6 +274,7 @@ extern const char *w32_get_string_resource (void *v_rdb,
274extern frame_parm_handler w32_frame_parm_handlers[]; 274extern frame_parm_handler w32_frame_parm_handlers[];
275extern void w32_default_font_parameter (struct frame* f, Lisp_Object parms); 275extern void w32_default_font_parameter (struct frame* f, Lisp_Object parms);
276extern Lisp_Object w32_process_dnd_data (int format, void *pDataObj); 276extern Lisp_Object w32_process_dnd_data (int format, void *pDataObj);
277extern void w32_register_for_sleep_notifications();
277 278
278 279
279#define PIX_TYPE COLORREF 280#define PIX_TYPE COLORREF