/src/pidgin/libpurple/idle.c
Line | Count | Source |
1 | | /* |
2 | | * purple |
3 | | * |
4 | | * Purple is the legal property of its developers, whose names are too numerous |
5 | | * to list here. Please refer to the COPYRIGHT file distributed with this |
6 | | * source distribution. |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 2 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
21 | | * |
22 | | */ |
23 | | #include "internal.h" |
24 | | |
25 | | #include "connection.h" |
26 | | #include "debug.h" |
27 | | #include "eventloop.h" |
28 | | #include "idle.h" |
29 | | #include "log.h" |
30 | | #include "prefs.h" |
31 | | #include "savedstatuses.h" |
32 | | #include "signals.h" |
33 | | |
34 | | typedef enum |
35 | | { |
36 | | PURPLE_IDLE_NOT_AWAY = 0, |
37 | | PURPLE_IDLE_AUTO_AWAY, |
38 | | PURPLE_IDLE_AWAY_BUT_NOT_AUTO_AWAY |
39 | | |
40 | | } PurpleAutoAwayState; |
41 | | |
42 | | static PurpleIdleUiOps *idle_ui_ops = NULL; |
43 | | |
44 | | /** |
45 | | * This is needed for the I'dle Mak'er plugin to work correctly. We |
46 | | * use it to determine if we're the ones who set our accounts idle |
47 | | * or if someone else did it (the I'dle Mak'er plugin, for example). |
48 | | * Basically we just keep track of which accounts were set idle by us, |
49 | | * and then we'll only set these specific accounts unidle when the |
50 | | * user returns. |
51 | | */ |
52 | | static GList *idled_accts = NULL; |
53 | | |
54 | | static guint idle_timer = 0; |
55 | | |
56 | | static time_t last_active_time = 0; |
57 | | |
58 | | static void |
59 | | set_account_idle(PurpleAccount *account, int time_idle) |
60 | 0 | { |
61 | 0 | PurplePresence *presence; |
62 | |
|
63 | 0 | presence = purple_account_get_presence(account); |
64 | |
|
65 | 0 | if (purple_presence_is_idle(presence)) |
66 | | /* This account is already idle! */ |
67 | 0 | return; |
68 | | |
69 | 0 | purple_debug_info("idle", "Setting %s idle %d seconds\n", |
70 | 0 | purple_account_get_username(account), time_idle); |
71 | 0 | purple_presence_set_idle(presence, TRUE, time(NULL) - time_idle); |
72 | 0 | idled_accts = g_list_prepend(idled_accts, account); |
73 | 0 | } |
74 | | |
75 | | static void |
76 | | set_account_unidle(PurpleAccount *account) |
77 | 0 | { |
78 | 0 | PurplePresence *presence; |
79 | |
|
80 | 0 | presence = purple_account_get_presence(account); |
81 | |
|
82 | 0 | idled_accts = g_list_remove(idled_accts, account); |
83 | |
|
84 | 0 | if (!purple_presence_is_idle(presence)) |
85 | | /* This account is already unidle! */ |
86 | 0 | return; |
87 | | |
88 | 0 | purple_debug_info("idle", "Setting %s unidle\n", |
89 | 0 | purple_account_get_username(account)); |
90 | 0 | purple_presence_set_idle(presence, FALSE, 0); |
91 | 0 | } |
92 | | |
93 | | |
94 | | static gboolean no_away = FALSE; |
95 | | static gint time_until_next_idle_event; |
96 | | /* |
97 | | * This function should be called when you think your idle state |
98 | | * may have changed. Maybe you're over the 10-minute mark and |
99 | | * Purple should start reporting idle time to the server. Maybe |
100 | | * you've returned from being idle. Maybe your auto-away message |
101 | | * should be set. |
102 | | * |
103 | | * There is no harm to calling this many many times, other than |
104 | | * it will be kinda slow. This is called by a timer set when |
105 | | * Purple starts. It is also called when you send an IM, a chat, etc. |
106 | | * |
107 | | * This function has 3 sections. |
108 | | * 1. Get your idle time. It will query XScreenSaver or Windows |
109 | | * or use the Purple idle time. Whatever. |
110 | | * 2. Set or unset your auto-away message. |
111 | | * 3. Report your current idle time to the IM server. |
112 | | */ |
113 | | |
114 | | static void |
115 | | check_idleness(void) |
116 | 0 | { |
117 | 0 | time_t time_idle; |
118 | 0 | gboolean auto_away; |
119 | 0 | const gchar *idle_reporting; |
120 | 0 | gboolean report_idle = TRUE; |
121 | 0 | gint away_seconds = 0; |
122 | 0 | gint idle_recheck_interval = 0; |
123 | 0 | gint idle_poll_seconds = purple_prefs_get_int("/purple/away/mins_before_away") * 60; |
124 | 0 | purple_signal_emit(purple_blist_get_handle(), "update-idle"); |
125 | |
|
126 | 0 | idle_reporting = purple_prefs_get_string("/purple/away/idle_reporting"); |
127 | 0 | auto_away = purple_prefs_get_bool("/purple/away/away_when_idle"); |
128 | |
|
129 | 0 | if (purple_strequal(idle_reporting, "system") && |
130 | 0 | (idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL)) |
131 | 0 | { |
132 | | /* Use system idle time (mouse or keyboard movement, etc.) */ |
133 | 0 | time_idle = idle_ui_ops->get_time_idle(); |
134 | 0 | idle_recheck_interval = 1; |
135 | 0 | } |
136 | 0 | else if (purple_strequal(idle_reporting, "purple")) |
137 | 0 | { |
138 | | /* Use 'Purple idle' */ |
139 | 0 | time_idle = time(NULL) - last_active_time; |
140 | 0 | idle_recheck_interval = 0; |
141 | 0 | } |
142 | 0 | else |
143 | 0 | { |
144 | | /* Don't report idle time */ |
145 | 0 | report_idle = FALSE; |
146 | | |
147 | | /* If we're not reporting idle, we can still do auto-away. |
148 | | * First try "system" and if that isn't possible, use "purple" */ |
149 | 0 | if (auto_away) |
150 | 0 | { |
151 | 0 | if ((idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL)) |
152 | 0 | { |
153 | 0 | time_idle = idle_ui_ops->get_time_idle(); |
154 | 0 | idle_recheck_interval = 1; |
155 | 0 | } |
156 | 0 | else |
157 | 0 | { |
158 | 0 | time_idle = time(NULL) - last_active_time; |
159 | 0 | idle_recheck_interval = 0; |
160 | 0 | } |
161 | 0 | } |
162 | 0 | else |
163 | 0 | { |
164 | 0 | if (!no_away) |
165 | 0 | { |
166 | 0 | no_away = TRUE; |
167 | 0 | purple_savedstatus_set_idleaway(FALSE); |
168 | 0 | } |
169 | 0 | time_until_next_idle_event = 0; |
170 | 0 | return; |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | 0 | time_until_next_idle_event = idle_poll_seconds - time_idle; |
175 | 0 | if (time_until_next_idle_event < 0) |
176 | 0 | { |
177 | | /* If we're already idle, check again as appropriate. */ |
178 | 0 | time_until_next_idle_event = idle_recheck_interval; |
179 | 0 | } |
180 | |
|
181 | 0 | if (auto_away || !no_away) |
182 | 0 | away_seconds = 60 * purple_prefs_get_int("/purple/away/mins_before_away"); |
183 | |
|
184 | 0 | if (auto_away && time_idle > away_seconds) |
185 | 0 | { |
186 | 0 | purple_savedstatus_set_idleaway(TRUE); |
187 | 0 | no_away = FALSE; |
188 | 0 | } |
189 | 0 | else if (purple_savedstatus_is_idleaway() && time_idle < away_seconds) |
190 | 0 | { |
191 | 0 | purple_savedstatus_set_idleaway(FALSE); |
192 | 0 | if (time_until_next_idle_event == 0 || (away_seconds - time_idle) < time_until_next_idle_event) |
193 | 0 | time_until_next_idle_event = away_seconds - time_idle; |
194 | 0 | } |
195 | | |
196 | | /* Idle reporting stuff */ |
197 | 0 | if (report_idle && (time_idle >= idle_poll_seconds)) |
198 | 0 | { |
199 | 0 | GList *l; |
200 | 0 | for (l = purple_connections_get_all(); l != NULL; l = l->next) |
201 | 0 | { |
202 | 0 | PurpleConnection *gc = l->data; |
203 | 0 | set_account_idle(purple_connection_get_account(gc), time_idle); |
204 | 0 | } |
205 | 0 | } |
206 | 0 | else if (!report_idle || (time_idle < idle_poll_seconds )) |
207 | 0 | { |
208 | 0 | while (idled_accts != NULL) |
209 | 0 | set_account_unidle(idled_accts->data); |
210 | 0 | } |
211 | 0 | } |
212 | | |
213 | | |
214 | | /* |
215 | | * Check idle and set the timer to fire at the next idle-worth event |
216 | | */ |
217 | | static gboolean |
218 | | check_idleness_timer(void) |
219 | 0 | { |
220 | 0 | check_idleness(); |
221 | 0 | if (time_until_next_idle_event == 0) |
222 | 0 | idle_timer = 0; |
223 | 0 | else |
224 | 0 | { |
225 | | /* +1 for the boundary, |
226 | | * +1 more for g_timeout_add_seconds rounding. */ |
227 | 0 | idle_timer = purple_timeout_add_seconds(time_until_next_idle_event + 2, (GSourceFunc)check_idleness_timer, NULL); |
228 | 0 | } |
229 | 0 | return FALSE; |
230 | 0 | } |
231 | | |
232 | | static void |
233 | | im_msg_sent_cb(PurpleAccount *account, const char *receiver, |
234 | | const char *message, void *data) |
235 | 0 | { |
236 | | /* Check our idle time after an IM is sent */ |
237 | 0 | check_idleness(); |
238 | 0 | } |
239 | | |
240 | | static void |
241 | | signing_on_cb(PurpleConnection *gc, void *data) |
242 | 0 | { |
243 | | /* When signing on a new account, check if the account should be idle */ |
244 | 0 | check_idleness(); |
245 | 0 | } |
246 | | |
247 | | static void |
248 | | signing_off_cb(PurpleConnection *gc, void *data) |
249 | 0 | { |
250 | 0 | PurpleAccount *account; |
251 | |
|
252 | 0 | account = purple_connection_get_account(gc); |
253 | 0 | set_account_unidle(account); |
254 | 0 | } |
255 | | |
256 | | static void |
257 | | idle_reporting_cb(const char *name, PurplePrefType type, gconstpointer val, gpointer data) |
258 | 0 | { |
259 | 0 | if (idle_timer) |
260 | 0 | purple_timeout_remove(idle_timer); |
261 | 0 | idle_timer = 0; |
262 | 0 | check_idleness_timer(); |
263 | 0 | } |
264 | | |
265 | | void |
266 | | purple_idle_touch() |
267 | 0 | { |
268 | 0 | time(&last_active_time); |
269 | 0 | if (!no_away) |
270 | 0 | { |
271 | 0 | if (idle_timer) |
272 | 0 | purple_timeout_remove(idle_timer); |
273 | 0 | idle_timer = 0; |
274 | 0 | check_idleness_timer(); |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | | void |
279 | | purple_idle_set(time_t time) |
280 | 0 | { |
281 | 0 | last_active_time = time; |
282 | 0 | } |
283 | | |
284 | | void |
285 | | purple_idle_set_ui_ops(PurpleIdleUiOps *ops) |
286 | 0 | { |
287 | 0 | idle_ui_ops = ops; |
288 | 0 | } |
289 | | |
290 | | PurpleIdleUiOps * |
291 | | purple_idle_get_ui_ops(void) |
292 | 0 | { |
293 | 0 | return idle_ui_ops; |
294 | 0 | } |
295 | | |
296 | | static void * |
297 | | purple_idle_get_handle(void) |
298 | 0 | { |
299 | 0 | static int handle; |
300 | |
|
301 | 0 | return &handle; |
302 | 0 | } |
303 | | |
304 | | static gboolean _do_purple_idle_touch_cb(gpointer data) |
305 | 0 | { |
306 | 0 | int idle_poll_minutes = purple_prefs_get_int("/purple/away/mins_before_away"); |
307 | | |
308 | | /* +1 more for g_timeout_add_seconds rounding. */ |
309 | 0 | idle_timer = purple_timeout_add_seconds((idle_poll_minutes * 60) + 2, (GSourceFunc)check_idleness_timer, NULL); |
310 | |
|
311 | 0 | purple_idle_touch(); |
312 | |
|
313 | 0 | return FALSE; |
314 | 0 | } |
315 | | |
316 | | |
317 | | void |
318 | | purple_idle_init() |
319 | 0 | { |
320 | 0 | purple_signal_connect(purple_conversations_get_handle(), "sent-im-msg", |
321 | 0 | purple_idle_get_handle(), |
322 | 0 | PURPLE_CALLBACK(im_msg_sent_cb), NULL); |
323 | 0 | purple_signal_connect(purple_connections_get_handle(), "signing-on", |
324 | 0 | purple_idle_get_handle(), |
325 | 0 | PURPLE_CALLBACK(signing_on_cb), NULL); |
326 | 0 | purple_signal_connect(purple_connections_get_handle(), "signing-off", |
327 | 0 | purple_idle_get_handle(), |
328 | 0 | PURPLE_CALLBACK(signing_off_cb), NULL); |
329 | |
|
330 | 0 | purple_prefs_connect_callback(purple_idle_get_handle(), "/purple/away/idle_reporting", |
331 | 0 | idle_reporting_cb, NULL); |
332 | | |
333 | | /* Initialize the idleness asynchronously so it doesn't check idleness, |
334 | | * and potentially try to change the status before the UI is initialized */ |
335 | 0 | purple_timeout_add(0, _do_purple_idle_touch_cb, NULL); |
336 | |
|
337 | 0 | } |
338 | | |
339 | | void |
340 | | purple_idle_uninit() |
341 | 0 | { |
342 | 0 | purple_signals_disconnect_by_handle(purple_idle_get_handle()); |
343 | 0 | purple_prefs_disconnect_by_handle(purple_idle_get_handle()); |
344 | | |
345 | | /* Remove the idle timer */ |
346 | 0 | if (idle_timer > 0) |
347 | 0 | purple_timeout_remove(idle_timer); |
348 | 0 | idle_timer = 0; |
349 | 0 | } |