Back to index

lightning-sunbird  0.9+nobinonly
nsAppShell.cpp
Go to the documentation of this file.
00001 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Markus G. Kuhn <mkuhn@acm.org>
00024  *   Richard Verhoeven <river@win.tue.nl>
00025  *   Frank Tang <ftang@netscape.com> adopt into mozilla
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "prmon.h"
00042 #include "plhash.h"
00043 #include "nscore.h"
00044 #include "nsCOMPtr.h"
00045 #include "nsAppShell.h"
00046 #include "nsIAppShell.h"
00047 #include "nsIServiceManager.h"
00048 #include "nsIEventQueueService.h"
00049 #include "nsGtkEventHandler.h"
00050 #include <stdlib.h>
00051 #include <gdk/gdkx.h>
00052 
00053 #include "nsIWidget.h"
00054 
00055 #include "glib.h"
00056 #include "nsVoidArray.h"
00057 
00058 static PLHashTable *sQueueHashTable = nsnull;
00059 static PLHashTable *sCountHashTable = nsnull;
00060 static nsVoidArray *sEventQueueList = nsnull;
00061 
00062 struct OurGdkIOClosure {
00063   GdkInputFunction  function;
00064   gpointer          data;
00065 };
00066 
00067 static gboolean
00068 our_gdk_io_invoke(GIOChannel* source, GIOCondition condition, gpointer data)
00069 {
00070   OurGdkIOClosure* ioc = (OurGdkIOClosure*) data;
00071   if (ioc) {
00072     (*ioc->function)(ioc->data, g_io_channel_unix_get_fd(source),
00073                      GDK_INPUT_READ);
00074   }
00075   return TRUE;
00076 }
00077 
00078 static void
00079 our_gdk_io_destroy(gpointer data)
00080 {
00081 #ifdef DEBUG_APPSHELL
00082   printf("our_gdk_io_destroy()\n");
00083 #endif
00084   OurGdkIOClosure* ioc = (OurGdkIOClosure*) data;
00085   if (ioc) {
00086     g_free(ioc);
00087   }
00088 }
00089 
00090 static gint
00091 our_gdk_input_add (gint              source,
00092                    GdkInputFunction  function,
00093                    gpointer          data,
00094                    gint              priority)
00095 {
00096 #ifdef DEBUG_APPSHELL
00097   printf("our_gdk_input_add()\n");
00098 #endif
00099   guint result;
00100   OurGdkIOClosure *closure = g_new (OurGdkIOClosure, 1);
00101   GIOChannel *channel;
00102 
00103   closure->function = function;
00104   closure->data = data;
00105 
00106   channel = g_io_channel_unix_new (source);
00107   result = g_io_add_watch_full (channel, priority, G_IO_IN,
00108                                 our_gdk_io_invoke,
00109                                 closure, our_gdk_io_destroy);
00110   g_io_channel_unref (channel);
00111 
00112   return result;
00113 }
00114 
00115 // wrapper so we can call a macro
00116 static unsigned long getNextRequest (void *aClosure) {
00117   return NextRequest(GDK_DISPLAY());
00118 }
00119 
00120 
00121 //-------------------------------------------------------------------------
00122 //
00123 // XPCOM CIDs
00124 //
00125 //-------------------------------------------------------------------------
00126 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00127 
00128 
00129 //-------------------------------------------------------------------------
00130 //
00131 // nsAppShell constructor
00132 //
00133 //-------------------------------------------------------------------------
00134 nsAppShell::nsAppShell()
00135 {
00136 #ifdef DEBUG_APPSHELL
00137   printf("nsAppShell::nsAppShell()\n");
00138 #endif
00139   if (!sEventQueueList)
00140     sEventQueueList = new nsVoidArray();
00141 }
00142 
00143 //-------------------------------------------------------------------------
00144 //
00145 // nsAppShell destructor
00146 //
00147 //-------------------------------------------------------------------------
00148 nsAppShell::~nsAppShell()
00149 {
00150 #ifdef DEBUG_APPSHELL
00151   printf("nsAppShell::~nsAppShell()\n");
00152 #endif
00153 }
00154 
00155 /* static */ void
00156 nsAppShell::ReleaseGlobals()
00157 {
00158   if (sQueueHashTable) {
00159     PL_HashTableDestroy(sQueueHashTable);
00160     sQueueHashTable = nsnull;
00161   }
00162   if (sCountHashTable) {
00163     PL_HashTableDestroy(sCountHashTable);
00164     sCountHashTable = nsnull;
00165   }
00166   if (sEventQueueList) {
00167     delete sEventQueueList;
00168     sEventQueueList = nsnull;
00169   }
00170 }
00171 
00172 //-------------------------------------------------------------------------
00173 //
00174 // nsISupports implementation macro
00175 //
00176 //-------------------------------------------------------------------------
00177 
00178 NS_IMPL_ISUPPORTS1(nsAppShell, nsIAppShell)
00179 
00180 static void event_processor_callback(gpointer data,
00181                                      gint source,
00182                                      GdkInputCondition condition)
00183 {
00184   nsIEventQueue *eventQueue = (nsIEventQueue*)data;
00185   if (eventQueue)
00186       eventQueue->ProcessPendingEvents();
00187   
00188 }
00189 
00190 //-------------------------------------------------------------------------
00191 //
00192 // Create the application shell
00193 //
00194 //-------------------------------------------------------------------------
00195 
00196 NS_IMETHODIMP nsAppShell::Create(int *bac, char **bav)
00197 {
00198   return NS_OK;
00199 }
00200 
00201 //-------------------------------------------------------------------------
00202 //
00203 // Spinup - do any preparation necessary for running a message loop
00204 //
00205 //-------------------------------------------------------------------------
00206 NS_IMETHODIMP nsAppShell::Spinup()
00207 {
00208   nsresult   rv = NS_OK;
00209 
00210 #ifdef DEBUG_APPSHELL
00211   printf("nsAppShell::Spinup()\n");
00212 #endif
00213 
00214   // Get the event queue service
00215   nsCOMPtr<nsIEventQueueService> eventQService = do_GetService(kEventQueueServiceCID, &rv);
00216 
00217   if (NS_FAILED(rv)) {
00218     NS_ASSERTION("Could not obtain event queue service", PR_FALSE);
00219     return rv;
00220   }
00221 
00222   //Get the event queue for the thread.
00223   rv = eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQueue));
00224   
00225   // If we got an event queue, use it.
00226   if (mEventQueue)
00227     goto done;
00228 
00229   // otherwise create a new event queue for the thread
00230   rv = eventQService->CreateThreadEventQueue();
00231   if (NS_FAILED(rv)) {
00232     NS_ASSERTION("Could not create the thread event queue", PR_FALSE);
00233     return rv;
00234   }
00235 
00236   // Ask again nicely for the event queue now that we have created one.
00237   rv = eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQueue));
00238 
00239   // XXX shouldn't this be automatic?
00240  done:
00241   ListenToEventQueue(mEventQueue, PR_TRUE);
00242 
00243   return rv;
00244 }
00245 
00246 //-------------------------------------------------------------------------
00247 //
00248 // Spindown - do any cleanup necessary for finishing a message loop
00249 //
00250 //-------------------------------------------------------------------------
00251 NS_IMETHODIMP nsAppShell::Spindown()
00252 {
00253 #ifdef DEBUG_APPSHELL
00254   printf("nsAppShell::Spindown()\n");
00255 #endif
00256   if (mEventQueue) {
00257     ListenToEventQueue(mEventQueue, PR_FALSE);
00258     mEventQueue->ProcessPendingEvents();
00259     mEventQueue = nsnull;
00260   }
00261   return NS_OK;
00262 }
00263 
00264 #ifdef NS_TRACE_MALLOC
00265 #include "nsTraceMalloc.h"
00266 
00267 static gint
00268 tm_flush_logfiles(gpointer data)
00269 {
00270   NS_TraceMallocFlushLogfiles();
00271   return 1;
00272 }
00273 #endif
00274 
00275 //-------------------------------------------------------------------------
00276 //
00277 // Run
00278 //
00279 //-------------------------------------------------------------------------
00280 NS_IMETHODIMP nsAppShell::Run()
00281 {
00282   if (!mEventQueue)
00283     Spinup();
00284   
00285   if (!mEventQueue)
00286     return NS_ERROR_NOT_INITIALIZED;
00287 
00288 #ifdef NS_TRACE_MALLOC
00289   gtk_idle_add(tm_flush_logfiles, nsnull);
00290 #endif
00291 
00292   // kick up gtk_main.  this won't return until gtk_main_quit is called
00293   gtk_main();
00294 
00295   Spindown();
00296 
00297   return NS_OK; 
00298 }
00299 
00300 //-------------------------------------------------------------------------
00301 //
00302 // Exit a message handler loop
00303 //
00304 //-------------------------------------------------------------------------
00305 
00306 NS_IMETHODIMP nsAppShell::Exit()
00307 {
00308   gtk_main_quit();
00309   return NS_OK;
00310 }
00311 
00312 // does nothing. used by xp code with non-gtk expectations.
00313 // this method will be removed once xp eventloops are working.
00314 NS_IMETHODIMP nsAppShell::GetNativeEvent(PRBool &aRealEvent, void *& aEvent)
00315 {
00316   aRealEvent = PR_FALSE;
00317   aEvent = 0;
00318 
00319   return NS_OK;
00320 }
00321 
00322 // simply executes one iteration of the event loop. used by xp code with
00323 // non-gtk expectations.
00324 // this method will be removed once xp eventloops are working.
00325 NS_IMETHODIMP nsAppShell::DispatchNativeEvent(PRBool aRealEvent, void *aEvent)
00326 {
00327   if (!mEventQueue)
00328     return NS_ERROR_NOT_INITIALIZED;
00329 
00330   g_main_iteration(PR_TRUE);
00331 
00332   return NS_OK;
00333 }
00334 
00335 #define NUMBER_HASH_KEY(_num) ((PLHashNumber) _num)
00336 
00337 static PLHashNumber
00338 IntHashKey(PRInt32 key)
00339 {
00340   return NUMBER_HASH_KEY(key);
00341 }
00342 
00343 NS_IMETHODIMP nsAppShell::ListenToEventQueue(nsIEventQueue *aQueue,
00344                                              PRBool aListen)
00345 {
00346 #ifdef DEBUG_APPSHELL
00347   printf("ListenToEventQueue(%p, %d) this=%p\n", aQueue, aListen, this);
00348 #endif
00349   if (!sQueueHashTable) {
00350     sQueueHashTable = PL_NewHashTable(3, (PLHashFunction)IntHashKey,
00351                                       PL_CompareValues, PL_CompareValues, 0, 0);
00352   }
00353   if (!sCountHashTable) {
00354     sCountHashTable = PL_NewHashTable(3, (PLHashFunction)IntHashKey,
00355                                       PL_CompareValues, PL_CompareValues, 0, 0);
00356   }    
00357 
00358   if (aListen) {
00359     /* add listener */
00360     PRInt32 key = aQueue->GetEventQueueSelectFD();
00361 
00362     /* only add if we arn't already in the table */
00363     if (!PL_HashTableLookup(sQueueHashTable, GINT_TO_POINTER(key))) {
00364       gint tag;
00365       tag = our_gdk_input_add(aQueue->GetEventQueueSelectFD(),
00366                               event_processor_callback,
00367                               aQueue,
00368                               G_PRIORITY_HIGH_IDLE);
00369       if (tag >= 0) {
00370         PL_HashTableAdd(sQueueHashTable, GINT_TO_POINTER(key), GINT_TO_POINTER(tag));
00371       }
00372       PLEventQueue *plqueue;
00373       aQueue->GetPLEventQueue(&plqueue);
00374       PL_RegisterEventIDFunc(plqueue, getNextRequest, 0);
00375       sEventQueueList->AppendElement(plqueue);
00376     }
00377     /* bump up the count */
00378     gint count = GPOINTER_TO_INT(PL_HashTableLookup(sCountHashTable, GINT_TO_POINTER(key)));
00379     PL_HashTableAdd(sCountHashTable, GINT_TO_POINTER(key), GINT_TO_POINTER(count+1));
00380   } else {
00381     /* remove listener */
00382     PRInt32 key = aQueue->GetEventQueueSelectFD();
00383     
00384     PLEventQueue *plqueue;
00385     aQueue->GetPLEventQueue(&plqueue);
00386     PL_UnregisterEventIDFunc(plqueue);
00387     sEventQueueList->RemoveElement(plqueue);
00388 
00389     gint count = GPOINTER_TO_INT(PL_HashTableLookup(sCountHashTable, GINT_TO_POINTER(key)));
00390     if (count - 1 == 0) {
00391       gint tag = GPOINTER_TO_INT(PL_HashTableLookup(sQueueHashTable, GINT_TO_POINTER(key)));
00392       if (tag > 0) {
00393         g_source_remove(tag);
00394         PL_HashTableRemove(sQueueHashTable, GINT_TO_POINTER(key));
00395       }
00396     }
00397     PL_HashTableAdd(sCountHashTable, GINT_TO_POINTER(key), GINT_TO_POINTER(count-1));
00398 
00399   }
00400 
00401   return NS_OK;
00402 }
00403 
00404 PRBool processQueue(void *aElement, void *aData)
00405 {
00406   PLEventQueue *queue = (PLEventQueue *) aElement;
00407   unsigned int  id = NS_PTR_TO_INT32(aData);
00408   PL_ProcessEventsBeforeID(queue, id);
00409   return PR_TRUE;
00410 }
00411 
00412 void
00413 nsAppShell::ProcessBeforeID(unsigned long aID)
00414 {
00415   if (sEventQueueList)
00416     sEventQueueList->EnumerateForwards(processQueue, (void *)aID);
00417 }