Merge heads.
[pidgin-git.git] / libpurple / idle.c
blob95816c947fa5c1fc55af12294dea6483dc23f880
1 /*
2 * purple
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.
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.
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.
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
23 #include "internal.h"
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"
34 typedef enum
36 PURPLE_IDLE_NOT_AWAY = 0,
37 PURPLE_IDLE_AUTO_AWAY,
38 PURPLE_IDLE_AWAY_BUT_NOT_AUTO_AWAY
40 } PurpleAutoAwayState;
42 static PurpleIdleUiOps *idle_ui_ops = NULL;
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.
52 static GList *idled_accts = NULL;
54 static guint idle_timer = 0;
56 static time_t last_active_time = 0;
58 static void
59 set_account_idle(PurpleAccount *account, int time_idle)
61 PurplePresence *presence;
63 presence = purple_account_get_presence(account);
65 if (purple_presence_is_idle(presence))
66 /* This account is already idle! */
67 return;
69 purple_debug_info("idle", "Setting %s idle %d seconds\n",
70 purple_account_get_username(account), time_idle);
71 purple_presence_set_idle(presence, TRUE, time(NULL) - time_idle);
72 idled_accts = g_list_prepend(idled_accts, account);
75 static void
76 set_account_unidle(PurpleAccount *account)
78 PurplePresence *presence;
80 presence = purple_account_get_presence(account);
82 idled_accts = g_list_remove(idled_accts, account);
84 if (!purple_presence_is_idle(presence))
85 /* This account is already unidle! */
86 return;
88 purple_debug_info("idle", "Setting %s unidle\n",
89 purple_account_get_username(account));
90 purple_presence_set_idle(presence, FALSE, 0);
94 static gboolean no_away = FALSE;
95 static gint time_until_next_idle_event;
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.
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.
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.
114 static void
115 check_idleness(void)
117 time_t time_idle = 0;
118 gboolean auto_away;
119 const gchar *idle_reporting;
120 gboolean report_idle = TRUE;
121 gint away_seconds = 0;
122 gint idle_recheck_interval = 0;
123 gint idle_poll_seconds = purple_prefs_get_int("/purple/away/mins_before_away") * 60;
124 purple_signal_emit(purple_blist_get_handle(), "update-idle");
126 idle_reporting = purple_prefs_get_string("/purple/away/idle_reporting");
127 auto_away = purple_prefs_get_bool("/purple/away/away_when_idle");
129 if (purple_strequal(idle_reporting, "system") &&
130 (idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL))
132 /* Use system idle time (mouse or keyboard movement, etc.) */
133 time_idle = idle_ui_ops->get_time_idle();
134 idle_recheck_interval = 1;
136 else if (purple_strequal(idle_reporting, "purple"))
138 /* Use 'Purple idle' */
139 time_idle = time(NULL) - last_active_time;
140 idle_recheck_interval = 0;
142 else
144 /* Don't report idle time */
145 report_idle = FALSE;
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 if (auto_away)
151 if ((idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL))
153 time_idle = idle_ui_ops->get_time_idle();
154 idle_recheck_interval = 1;
156 else
158 time_idle = time(NULL) - last_active_time;
159 idle_recheck_interval = 0;
162 else
164 if (!no_away)
166 no_away = TRUE;
167 purple_savedstatus_set_idleaway(FALSE);
169 time_until_next_idle_event = 0;
170 return;
174 time_until_next_idle_event = idle_poll_seconds - time_idle;
175 if (time_until_next_idle_event < 0)
177 /* If we're already idle, check again as appropriate. */
178 time_until_next_idle_event = idle_recheck_interval;
181 if (auto_away || !no_away)
182 away_seconds = 60 * purple_prefs_get_int("/purple/away/mins_before_away");
184 if (auto_away && time_idle > away_seconds)
186 purple_savedstatus_set_idleaway(TRUE);
187 no_away = FALSE;
189 else if (purple_savedstatus_is_idleaway() && time_idle < away_seconds)
191 purple_savedstatus_set_idleaway(FALSE);
192 if (time_until_next_idle_event == 0 || (away_seconds - time_idle) < time_until_next_idle_event)
193 time_until_next_idle_event = away_seconds - time_idle;
196 /* Idle reporting stuff */
197 if (report_idle && (time_idle >= idle_poll_seconds))
199 GList *l;
200 for (l = purple_connections_get_all(); l != NULL; l = l->next)
202 PurpleConnection *gc = l->data;
203 set_account_idle(purple_connection_get_account(gc), time_idle);
206 else if (!report_idle || (time_idle < idle_poll_seconds ))
208 while (idled_accts != NULL)
209 set_account_unidle(idled_accts->data);
215 * Check idle and set the timer to fire at the next idle-worth event
217 static gboolean
218 check_idleness_timer(void)
220 check_idleness();
221 if (time_until_next_idle_event == 0)
222 idle_timer = 0;
223 else
225 /* +1 for the boundary,
226 * +1 more for g_timeout_add_seconds rounding. */
227 idle_timer = g_timeout_add_seconds(time_until_next_idle_event + 2, (GSourceFunc)check_idleness_timer, NULL);
229 return FALSE;
232 static void
233 im_msg_sent_cb(PurpleAccount *account, PurpleMessage *msg, void *data)
235 /* Check our idle time after an IM is sent */
236 check_idleness();
239 static void
240 signing_on_cb(PurpleConnection *gc, void *data)
242 /* When signing on a new account, check if the account should be idle */
243 check_idleness();
246 static void
247 signing_off_cb(PurpleConnection *gc, void *data)
249 PurpleAccount *account;
251 account = purple_connection_get_account(gc);
252 set_account_unidle(account);
255 static void
256 idle_reporting_cb(const char *name, PurplePrefType type, gconstpointer val, gpointer data)
258 if (idle_timer)
259 g_source_remove(idle_timer);
260 idle_timer = 0;
261 check_idleness_timer();
264 void
265 purple_idle_touch()
267 time(&last_active_time);
268 if (!no_away)
270 if (idle_timer)
271 g_source_remove(idle_timer);
272 idle_timer = 0;
273 check_idleness_timer();
277 void
278 purple_idle_set(time_t time)
280 last_active_time = time;
283 static PurpleIdleUiOps *
284 purple_idle_ui_ops_copy(PurpleIdleUiOps *ops)
286 PurpleIdleUiOps *ops_new;
288 g_return_val_if_fail(ops != NULL, NULL);
290 ops_new = g_new(PurpleIdleUiOps, 1);
291 *ops_new = *ops;
293 return ops_new;
296 GType
297 purple_idle_ui_ops_get_type(void)
299 static GType type = 0;
301 if (type == 0) {
302 type = g_boxed_type_register_static("PurpleIdleUiOps",
303 (GBoxedCopyFunc)purple_idle_ui_ops_copy,
304 (GBoxedFreeFunc)g_free);
307 return type;
310 void
311 purple_idle_set_ui_ops(PurpleIdleUiOps *ops)
313 idle_ui_ops = ops;
316 PurpleIdleUiOps *
317 purple_idle_get_ui_ops(void)
319 return idle_ui_ops;
322 static void *
323 purple_idle_get_handle(void)
325 static int handle;
327 return &handle;
330 static gboolean _do_purple_idle_touch_cb(gpointer data)
332 int idle_poll_minutes = purple_prefs_get_int("/purple/away/mins_before_away");
334 /* +1 more for g_timeout_add_seconds rounding. */
335 idle_timer = g_timeout_add_seconds((idle_poll_minutes * 60) + 2, (GSourceFunc)check_idleness_timer, NULL);
337 purple_idle_touch();
339 return FALSE;
343 void
344 purple_idle_init()
346 purple_signal_connect(purple_conversations_get_handle(), "sent-im-msg",
347 purple_idle_get_handle(),
348 PURPLE_CALLBACK(im_msg_sent_cb), NULL);
349 purple_signal_connect(purple_connections_get_handle(), "signing-on",
350 purple_idle_get_handle(),
351 PURPLE_CALLBACK(signing_on_cb), NULL);
352 purple_signal_connect(purple_connections_get_handle(), "signing-off",
353 purple_idle_get_handle(),
354 PURPLE_CALLBACK(signing_off_cb), NULL);
356 purple_prefs_connect_callback(purple_idle_get_handle(), "/purple/away/idle_reporting",
357 idle_reporting_cb, NULL);
359 /* Initialize the idleness asynchronously so it doesn't check idleness,
360 * and potentially try to change the status before the UI is initialized */
361 g_timeout_add(0, _do_purple_idle_touch_cb, NULL);
365 void
366 purple_idle_uninit()
368 purple_signals_disconnect_by_handle(purple_idle_get_handle());
369 purple_prefs_disconnect_by_handle(purple_idle_get_handle());
371 /* Remove the idle timer */
372 if (idle_timer > 0)
373 g_source_remove(idle_timer);
374 idle_timer = 0;