2 This file is part of systemd.
4 Copyright 2012 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/ioctl.h>
25 #include <linux/input.h>
27 #include "sd-messages.h"
29 #include "alloc-util.h"
31 #include "logind-button.h"
32 #include "string-util.h"
35 Button
* button_new(Manager
*m
, const char *name
) {
45 b
->name
= strdup(name
);
49 if (hashmap_put(m
->buttons
, b
->name
, b
) < 0) {
60 void button_free(Button
*b
) {
63 hashmap_remove(b
->manager
->buttons
, b
->name
);
65 sd_event_source_unref(b
->io_event_source
);
66 sd_event_source_unref(b
->check_event_source
);
69 /* If the device has been unplugged close() returns
70 * ENODEV, let's ignore this, hence we don't use
79 int button_set_seat(Button
*b
, const char *sn
) {
95 static void button_lid_switch_handle_action(Manager
*manager
, bool is_edge
) {
96 HandleAction handle_action
;
100 /* If we are docked, handle the lid switch differently */
101 if (manager_is_docked_or_external_displays(manager
))
102 handle_action
= manager
->handle_lid_switch_docked
;
104 handle_action
= manager
->handle_lid_switch
;
106 manager_handle_action(manager
, INHIBIT_HANDLE_LID_SWITCH
, handle_action
, manager
->lid_switch_ignore_inhibited
, is_edge
);
109 static int button_recheck(sd_event_source
*e
, void *userdata
) {
110 Button
*b
= userdata
;
113 assert(b
->lid_closed
);
115 button_lid_switch_handle_action(b
->manager
, false);
119 static int button_install_check_event_source(Button
*b
) {
123 /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
125 if (b
->check_event_source
)
128 r
= sd_event_add_post(b
->manager
->event
, &b
->check_event_source
, button_recheck
, b
);
132 return sd_event_source_set_priority(b
->check_event_source
, SD_EVENT_PRIORITY_IDLE
+1);
135 static int button_dispatch(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
136 Button
*b
= userdata
;
137 struct input_event ev
;
144 l
= read(b
->fd
, &ev
, sizeof(ev
));
146 return errno
!= EAGAIN
? -errno
: 0;
147 if ((size_t) l
< sizeof(ev
))
150 if (ev
.type
== EV_KEY
&& ev
.value
> 0) {
157 LOG_MESSAGE("Power key pressed."),
158 LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY
),
161 manager_handle_action(b
->manager
, INHIBIT_HANDLE_POWER_KEY
, b
->manager
->handle_power_key
, b
->manager
->power_key_ignore_inhibited
, true);
164 /* The kernel is a bit confused here:
166 KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend"
167 KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
172 LOG_MESSAGE("Suspend key pressed."),
173 LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY
),
176 manager_handle_action(b
->manager
, INHIBIT_HANDLE_SUSPEND_KEY
, b
->manager
->handle_suspend_key
, b
->manager
->suspend_key_ignore_inhibited
, true);
181 LOG_MESSAGE("Hibernate key pressed."),
182 LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY
),
185 manager_handle_action(b
->manager
, INHIBIT_HANDLE_HIBERNATE_KEY
, b
->manager
->handle_hibernate_key
, b
->manager
->hibernate_key_ignore_inhibited
, true);
189 } else if (ev
.type
== EV_SW
&& ev
.value
> 0) {
191 if (ev
.code
== SW_LID
) {
193 LOG_MESSAGE("Lid closed."),
194 LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED
),
197 b
->lid_closed
= true;
198 button_lid_switch_handle_action(b
->manager
, true);
199 button_install_check_event_source(b
);
201 } else if (ev
.code
== SW_DOCK
) {
203 LOG_MESSAGE("System docked."),
204 LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED
),
210 } else if (ev
.type
== EV_SW
&& ev
.value
== 0) {
212 if (ev
.code
== SW_LID
) {
214 LOG_MESSAGE("Lid opened."),
215 LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED
),
218 b
->lid_closed
= false;
219 b
->check_event_source
= sd_event_source_unref(b
->check_event_source
);
221 } else if (ev
.code
== SW_DOCK
) {
223 LOG_MESSAGE("System undocked."),
224 LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED
),
234 int button_open(Button
*b
) {
240 b
->fd
= safe_close(b
->fd
);
242 p
= strjoina("/dev/input/", b
->name
);
244 b
->fd
= open(p
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
|O_NONBLOCK
);
246 return log_warning_errno(errno
, "Failed to open %s: %m", b
->name
);
248 if (ioctl(b
->fd
, EVIOCGNAME(sizeof(name
)), name
) < 0) {
249 r
= log_error_errno(errno
, "Failed to get input name: %m");
253 r
= sd_event_add_io(b
->manager
->event
, &b
->io_event_source
, b
->fd
, EPOLLIN
, button_dispatch
, b
);
255 log_error_errno(r
, "Failed to add button event: %m");
259 log_info("Watching system buttons on /dev/input/%s (%s)", b
->name
, name
);
264 b
->fd
= safe_close(b
->fd
);
268 int button_check_switches(Button
*b
) {
269 uint8_t switches
[SW_MAX
/8+1] = {};
275 if (ioctl(b
->fd
, EVIOCGSW(sizeof(switches
)), switches
) < 0)
278 b
->lid_closed
= (switches
[SW_LID
/8] >> (SW_LID
% 8)) & 1;
279 b
->docked
= (switches
[SW_DOCK
/8] >> (SW_DOCK
% 8)) & 1;
282 button_install_check_event_source(b
);