2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / application.cpp
blob412789da04558a792e1818fd9f0f23b359d72c60
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * application.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include <config.h>
16 #include <glib/gstdio.h>
17 #include <fcntl.h>
18 #include <errno.h>
20 #include "application.h"
21 #include "runtime.h"
22 #include "deployment.h"
23 #include "utils.h"
24 #include "uri.h"
27 Application::Application ()
29 SetObjectType (Type::APPLICATION);
31 resource_root = NULL;
33 apply_default_style_cb = NULL;
34 get_default_template_root_cb = NULL;
35 apply_style_cb = NULL;
36 convert_keyframe_callback = NULL;
37 get_resource_cb = NULL;
40 Application::~Application ()
42 if (resource_root) {
43 Deployment::GetCurrent()->UntrackPath (resource_root);
44 RemoveDir (resource_root);
45 g_free (resource_root);
49 Application*
50 Application::GetCurrent ()
52 return Deployment::GetCurrent()->GetCurrentApplication();
55 void
56 Application::SetCurrent (Application *application)
58 Deployment::GetCurrent()->SetCurrentApplication (application);
61 void
62 Application::RegisterCallbacks (ApplyDefaultStyleCallback apply_default_style_cb,
63 ApplyStyleCallback apply_style_cb,
64 GetResourceCallback get_resource_cb,
65 ConvertKeyframeValueCallback convert_keyframe_callback,
66 GetDefaultTemplateRootCallback get_default_template_root_cb)
68 this->apply_default_style_cb = apply_default_style_cb;
69 this->get_default_template_root_cb = get_default_template_root_cb;
70 this->apply_style_cb = apply_style_cb;
71 this->convert_keyframe_callback = convert_keyframe_callback;
72 this->get_resource_cb = get_resource_cb;
75 void
76 Application::ApplyDefaultStyle (FrameworkElement *fwe, ManagedTypeInfo *key)
78 if (apply_default_style_cb)
79 apply_default_style_cb (fwe, key);
82 UIElement *
83 Application::GetDefaultTemplateRoot (ContentControl *ctrl)
85 if (get_default_template_root_cb)
86 return get_default_template_root_cb (ctrl);
87 return NULL;
90 void
91 Application::ApplyStyle (FrameworkElement *fwe, Style *style)
93 if (apply_style_cb)
94 apply_style_cb (fwe, style);
97 void
98 Application::ConvertKeyframeValue (Type::Kind kind, DependencyProperty *property, Value *original, Value *converted)
100 if (convert_keyframe_callback) {
101 convert_keyframe_callback (kind, property, original, converted);
102 } else {
103 converted = new Value (*original);
107 struct NotifyCtx {
108 gpointer user_data;
109 NotifyFunc notify_cb;
110 WriteFunc write_cb;
113 static void downloader_abort (gpointer data, void *ctx);
114 static void downloader_progress_changed (EventObject *sender, EventArgs *calldata, gpointer closure);
115 static void downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure);
116 static void downloader_failed (EventObject *sender, EventArgs *calldata, gpointer closure);
117 static void downloader_write (void *data, gint32 offset, gint32 n, void *closure);
118 static void downloader_notify_size (gint64 size, gpointer closure);
120 void
121 Application::GetResource (const char *resourceBase, const Uri *uri,
122 NotifyFunc notify_cb, WriteFunc write_cb,
123 DownloaderAccessPolicy policy,
124 Cancellable *cancellable, gpointer user_data)
126 if (!uri) {
127 g_warning ("Passing a null uri to Application::GetResource");
128 return;
131 if (get_resource_cb && uri && !uri->isAbsolute) {
132 char *url = uri->ToString ();
133 ManagedStreamCallbacks stream = get_resource_cb (resourceBase, url);
134 g_free (url);
136 if (stream.handle) {
137 if (notify_cb) {
138 notify_cb (NotifyStarted, NULL, user_data);
139 notify_cb (NotifySize, stream.Length (stream.handle), user_data);
142 if (write_cb) {
143 char buf[4096];
144 int offset = 0;
145 int nread;
147 if (stream.CanSeek (stream.handle))
148 stream.Seek (stream.handle, 0, 0);
150 do {
151 if ((nread = stream.Read (stream.handle, buf, 0, sizeof (buf))) <= 0)
152 break;
154 write_cb (buf, offset, nread, user_data);
155 offset += nread;
156 } while (true);
159 if (notify_cb)
160 notify_cb (NotifyCompleted, NULL, user_data);
162 stream.Close (stream.handle);
164 return;
168 //no get_resource_cb or empty stream
169 Downloader *downloader;
170 Surface *surface = Deployment::GetCurrent ()->GetSurface ();
171 if (!(downloader = surface->CreateDownloader ()))
172 return;
174 NotifyCtx *ctx = g_new (NotifyCtx, 1);
175 ctx->user_data = user_data;
176 ctx->notify_cb = notify_cb;
177 ctx->write_cb = write_cb;
179 if (notify_cb) {
180 downloader->AddHandler (Downloader::DownloadProgressChangedEvent, downloader_progress_changed, ctx);
181 downloader->AddHandler (Downloader::DownloadFailedEvent, downloader_failed, ctx);
182 downloader->AddHandler (Downloader::CompletedEvent, downloader_complete, ctx);
185 if (cancellable) {
186 cancellable->SetCancelFuncAndData (downloader_abort, downloader, ctx);
189 if (downloader->Completed ()) {
190 if (notify_cb)
191 notify_cb (NotifyCompleted, NULL, user_data);
192 } else {
193 if (!downloader->Started ()) {
194 downloader->Open ("GET", (Uri*)uri, policy);
195 downloader->SetStreamFunctions (downloader_write, downloader_notify_size, ctx);
196 downloader->Send ();
201 static void
202 downloader_notify_size (gint64 size, gpointer closure)
204 NotifyCtx *ctx = (NotifyCtx *) closure;
205 ctx->notify_cb (NotifySize, size, ctx->user_data);
208 static void
209 downloader_write (void *data, gint32 offset, gint32 n, void *closure)
211 NotifyCtx *ctx = (NotifyCtx *) closure;
212 ctx->write_cb (data, offset, n, ctx->user_data);
215 static void
216 downloader_abort (gpointer data, void *ctx)
218 Downloader *dl = (Downloader *) data;
219 NotifyCtx *nc = (NotifyCtx *)ctx;
220 dl->RemoveHandler (Downloader::DownloadProgressChangedEvent, downloader_progress_changed, nc);
221 dl->RemoveHandler (Downloader::DownloadFailedEvent, downloader_failed, nc);
222 dl->RemoveHandler (Downloader::CompletedEvent, downloader_complete, nc);
223 dl->Abort ();
226 static void
227 downloader_progress_changed (EventObject *sender, EventArgs *calldata, gpointer closure)
229 NotifyCtx *ctx = (NotifyCtx *) closure;
230 Downloader *dl = (Downloader *) sender;
231 ctx->notify_cb (NotifyProgressChanged, (gint64) (100 * dl->GetDownloadProgress ()), ctx->user_data);
234 static void
235 downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
237 NotifyCtx *ctx = (NotifyCtx *) closure;
238 ctx->notify_cb (NotifyCompleted, NULL, ctx->user_data);
239 g_free (ctx);
240 ((Downloader *) sender)->unref_delayed ();
243 static void
244 downloader_failed (EventObject *sender, EventArgs *calldata, gpointer closure)
246 NotifyCtx *ctx = (NotifyCtx *) closure;
247 ctx->notify_cb (NotifyFailed, NULL, ctx->user_data);
248 g_free (ctx);
249 ((Downloader *) sender)->unref_delayed ();
252 //FIXME: nuke this!
253 char *
254 Application::GetResourceAsPath (const char *resourceBase, const Uri *uri)
256 char *dirname, *path, *filename, *url;
257 ManagedStreamCallbacks stream;
258 unzFile zipfile;
259 struct stat st;
260 char buf[4096];
261 int nread;
262 int fd;
264 if (!get_resource_cb || !uri || uri->isAbsolute)
265 return NULL;
267 // construct the path name for this resource
268 filename = uri->ToString ();
269 CanonicalizeFilename (filename, -1, CanonModeResource);
270 if (uri->GetQuery () != NULL) {
271 char *sc = strchr (filename, ';');
272 if (sc)
273 *sc = '/';
276 path = g_build_filename (GetResourceRoot(), filename, NULL);
277 g_free (filename);
279 if (g_stat (path, &st) != -1) {
280 // path exists, we're done
281 return path;
284 // create the directory for our resource (keeping the relative path intact)
285 dirname = g_path_get_dirname (path);
286 if (g_mkdir_with_parents (dirname, 0700) == -1 && errno != EEXIST) {
287 g_free (dirname);
288 g_free (path);
289 return NULL;
292 g_free (dirname);
294 url = uri->ToString ();
295 stream = get_resource_cb (resourceBase, url);
296 g_free (url);
298 if (!stream.handle) {
299 g_free (path);
300 return NULL;
303 // reset the stream to 0
304 if (stream.CanSeek (stream.handle))
305 stream.Seek (stream.handle, 0, SEEK_SET);
307 // create and save the buffer to disk
308 if ((fd = g_open (path, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) {
309 stream.Close (stream.handle);
310 g_free (path);
311 return NULL;
314 // write the stream to disk
315 do {
316 if ((nread = stream.Read (stream.handle, buf, 0, sizeof (buf))) <= 0)
317 break;
319 if (write_all (fd, buf, (size_t) nread) == -1) {
320 stream.Close (stream.handle);
321 g_unlink (path);
322 g_free (path);
323 close (fd);
325 return NULL;
327 } while (true);
329 stream.Close (stream.handle);
330 close (fd);
332 // check to see if the resource is zipped
333 if (!(zipfile = unzOpen (path))) {
334 // nope, not zipped...
335 return path;
338 // create a directory to contain our unzipped content
339 if (!(dirname = CreateTempDir (path))) {
340 unzClose (zipfile);
341 g_free (dirname);
342 g_unlink (path);
343 g_free (path);
344 return NULL;
347 // unzip the contents
348 if (!ExtractAll (zipfile, dirname, CanonModeResource)) {
349 RemoveDir (dirname);
350 unzClose (zipfile);
351 g_free (dirname);
352 g_unlink (path);
353 g_free (path);
354 return NULL;
357 unzClose (zipfile);
358 g_unlink (path);
360 if (g_rename (dirname, path) == -1) {
361 RemoveDir (dirname);
362 g_free (dirname);
363 g_free (path);
364 return NULL;
367 g_free (dirname);
369 return path;
372 const char*
373 Application::GetResourceRoot ()
375 if (!resource_root) {
376 char *buf = g_build_filename (g_get_tmp_dir (), "moonlight-app.XXXXXX", NULL);
377 // create a root temp directory for all files
378 if (!(resource_root = MakeTempDir (buf)))
379 g_free (buf);
380 Deployment::GetCurrent()->TrackPath (resource_root);
382 return resource_root;