1 /**
2 * @file motion.c
3 * @brief
4 *
5 * Copyright (C) 2009 Gummi Developers
6 * All Rights reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use,
12 * copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following
15 * conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <signal.h>
36
37 #ifndef WIN32
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #endif
41
42 #ifdef WIN32
43 #include <windows.h>
44 #endif
45
46 #include <glib.h>
47 #include <gtk/gtk.h>
48
49 #include "configfile.h"
50 #include "editor.h"
51 #include "environment.h"
52 #include "gui/gui-main.h"
53 #include "gui/gui-preview.h"
54 #include "latex.h"
55 #include "snippets.h"
56 #include "utils.h"
57
58 extern GummiGui* gui;
59 extern Gummi* gummi;
60
61 /* Typesetter pid */
62 pid_t typesetter_pid = 0;
63
motion_init(void)64 GuMotion* motion_init (void) {
65 GuMotion* m = g_new0 (GuMotion, 1);
66
67 m->key_press_timer = 0;
68 g_mutex_init(&m->signal_mutex);
69 g_mutex_init(&m->compile_mutex);
70 g_cond_init(&m->compile_cv);
71 m->keep_running = TRUE;
72 m->keep_running = FALSE;
73 m->typesetter_pid = &typesetter_pid;
74
75 return m;
76 }
77
motion_start_compile_thread(GuMotion * m)78 void motion_start_compile_thread (GuMotion* m) {
79 m->keep_running = TRUE;
80 m->compile_thread = g_thread_new ("motion", motion_compile_thread, m);
81 }
82
motion_stop_compile_thread(GuMotion * m)83 void motion_stop_compile_thread (GuMotion* m) {
84 L_F_DEBUG;
85
86 m->keep_running = FALSE;
87 motion_do_compile(m);
88 g_thread_join(m->compile_thread);
89 }
90
motion_pause_compile_thread(GuMotion * m)91 void motion_pause_compile_thread (GuMotion* m) {
92 L_F_DEBUG;
93
94 m->pause = TRUE;
95 motion_do_compile(m);
96 }
97
motion_resume_compile_thread(GuMotion * m)98 void motion_resume_compile_thread (GuMotion* m) {
99 L_F_DEBUG;
100
101 m->pause = FALSE;
102 motion_do_compile(m);
103 }
104
motion_kill_typesetter(GuMotion * m)105 void motion_kill_typesetter (GuMotion* m) {
106 if (*m->typesetter_pid) {
107 gchar* command = NULL;
108 /* Kill children spawned by typesetter command/script, don't know
109 * how to do this programatically yet(glib doesn't not provides any
110 * function for killing a process), so use pkill for now. For
111 * win32 there's currently nothing we can do about it. */
112 #ifndef WIN32
113 command = g_strdup_printf("pkill -15 -P %d", *m->typesetter_pid);
114 system(command);
115 g_free(command);
116
117 /* Make sure typesetter command is terminated */
118 if (kill(*m->typesetter_pid, 15)) {
119 slog(L_ERROR, "Could not kill process: %s\n",
120 g_strerror(errno));
121 }
122 #else
123 if (!TerminateProcess(*m->typesetter_pid, 0)) {
124 gchar *msg = g_win32_error_message(GetLastError());
125 slog (L_ERROR, "Could not kill process: %s\n",
126 msg ? msg : "(null)");
127 g_free(msg);
128 }
129
130 #endif
131
132
133 slog(L_DEBUG, "Typeseter[pid=%d]: Killed\n", *m->typesetter_pid);
134 *m->typesetter_pid = 0;
135
136 /* XXX: Ugly hack: delay compile signal */
137 motion_start_timer (m);
138 }
139 }
140
motion_do_compile(gpointer user)141 gboolean motion_do_compile (gpointer user) {
142 L_F_DEBUG;
143 GuMotion* mc = GU_MOTION (user);
144
145 if (!g_mutex_trylock (&mc->signal_mutex)) goto ret;
146 g_cond_signal (&mc->compile_cv);
147 g_mutex_unlock (&mc->signal_mutex);
148
149 ret:
150 return (config_value_as_str_equals ("Compile", "scheme", "real_time"));
151 }
152
motion_compile_thread(gpointer data)153 gpointer motion_compile_thread (gpointer data) {
154 L_F_DEBUG;
155 GuMotion* mc = GU_MOTION (data);
156 GuEditor* editor = NULL;
157 GuLatex* latex = NULL;
158 gboolean precompile_ok = FALSE;
159 gchar *editortext;
160
161 latex = gummi_get_latex ();
162
163 while (TRUE) {
164 if (!g_mutex_trylock (&mc->compile_mutex)) continue;
165 slog (L_DEBUG, "Compile thread sleeping...\n");
166 g_cond_wait (&mc->compile_cv, &mc->compile_mutex);
167 slog (L_DEBUG, "Compile thread awoke.\n");
168
169 if (!(editor = gummi_get_active_editor ())) {
170 g_mutex_unlock (&mc->compile_mutex);
171 continue;
172 }
173 if (!mc->keep_running) {
174 g_mutex_unlock (&mc->compile_mutex);
175 g_thread_exit (NULL);
176 }
177
178 if (mc->pause) {
179 g_mutex_unlock (&mc->compile_mutex);
180 continue;
181 }
182
183 gdk_threads_enter ();
184 editortext = latex_update_workfile (editor);
185 precompile_ok = latex_precompile_check (editortext);
186 g_free (editortext);
187 gdk_threads_leave ();
188
189 if (!precompile_ok) {
190 gdk_threads_add_idle (on_document_error, "document_error");
191 g_mutex_unlock (&mc->compile_mutex);
192 continue;
193 }
194
195 latex_update_pdffile (latex, editor);
196
197 *mc->typesetter_pid = 0;
198 g_mutex_unlock (&mc->compile_mutex);
199
200 if (!mc->keep_running)
201 g_thread_exit (NULL);
202
203 gdk_threads_add_idle (on_document_compiled, editor);
204 continue;
205 }
206 }
207
motion_force_compile(GuMotion * mc)208 void motion_force_compile (GuMotion *mc) {
209 /* sort-of signal to force a compile run after certain actions that
210 * don't trigger the regular editor content change signals */
211 gummi->latex->modified_since_compile = TRUE;
212 motion_do_compile (mc);
213 }
214
motion_idle_cb(gpointer user)215 gboolean motion_idle_cb (gpointer user) {
216 GU_MOTION(user)->key_press_timer = 0;
217 if (gui->previewgui->preview_on_idle)
218 motion_do_compile (GU_MOTION (user));
219 return FALSE;
220 }
221
motion_start_timer(GuMotion * mc)222 void motion_start_timer (GuMotion* mc) {
223 motion_stop_timer (mc);
224 mc->key_press_timer = g_timeout_add_seconds (
225 config_get_integer ("Compile", "timer"),
226 motion_idle_cb, mc);
227 }
228
motion_stop_timer(GuMotion * mc)229 void motion_stop_timer (GuMotion* mc) {
230 if (mc->key_press_timer > 0) {
231 g_source_remove (mc->key_press_timer);
232 mc->key_press_timer = 0;
233 }
234 }
235
on_key_press_cb(GtkWidget * widget,GdkEventKey * event,void * user)236 gboolean on_key_press_cb (GtkWidget* widget, GdkEventKey* event, void* user) {
237 if (!event->is_modifier) {
238 motion_stop_timer (GU_MOTION (user));
239 }
240 if (config_get_boolean ("Interface", "snippets") &&
241 snippets_key_press_cb (gummi_get_snippets (),
242 gummi_get_active_editor (), event))
243 return TRUE;
244 return FALSE;
245 }
246
on_key_release_cb(GtkWidget * widget,GdkEventKey * event,void * user)247 gboolean on_key_release_cb (GtkWidget* widget, GdkEventKey* event, void* user) {
248 if (!event->is_modifier) {
249 motion_start_timer (GU_MOTION (user));
250 }
251 if (config_get_boolean ("Interface", "snippets") &&
252 snippets_key_release_cb (gummi_get_snippets (),
253 gummi_get_active_editor (), event))
254 return TRUE;
255 return FALSE;
256 }
257