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  *   Duncan Wilcox <duncan@be.com>
00024  *   Yannick Koehler <ykoehler@mythrium.com>
00025  *   Makoto Hamanaka <VYA04230@nifty.com>
00026  *   Fredrik Holmqvist <thesuckiestemail@yahoo.se>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsAppShell.h"
00043 #include "nsIEventQueueService.h"
00044 #include "nsIServiceManager.h"
00045 #include "nsIWidget.h"
00046 #include "nsIAppShell.h"
00047 #include "nsSwitchToUIThread.h"
00048 #include "plevent.h"
00049 #include "prprf.h"
00050 #include "nsGUIEvent.h"
00051 
00052 #include <Application.h>
00053 #include <stdlib.h>
00054 
00055 static int gBAppCount = 0;
00056 
00057 struct ThreadInterfaceData
00058 {
00059   void *data;
00060   thread_id waitingThread;
00061 };
00062 
00063 struct EventItem
00064 {
00065   int32 code;
00066   ThreadInterfaceData ifdata;
00067 };
00068 
00069 static sem_id my_find_sem(const char *name)
00070 {
00071   sem_id      ret = B_ERROR;
00072 
00073   /* Get the sem_info for every sempahore in this team. */
00074   sem_info info;
00075   int32 cookie = 0;
00076 
00077   while(get_next_sem_info(0, &cookie, &info) == B_OK)
00078   {
00079     if(strcmp(name, info.name) == 0)
00080     {
00081       ret = info.sem;
00082       break;
00083     }
00084   }
00085   return ret;
00086 }
00087 
00088 
00089 //-------------------------------------------------------------------------
00090 //
00091 // nsISupports implementation macro
00092 //
00093 //-------------------------------------------------------------------------
00094 NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00095 NS_IMPL_THREADSAFE_ISUPPORTS1(nsAppShell, nsIAppShell)
00096 
00097 //-------------------------------------------------------------------------
00098 //
00099 // nsAppShell constructor
00100 //
00101 //-------------------------------------------------------------------------
00102 nsAppShell::nsAppShell()  
00103        : is_port_error(false)
00104 { 
00105   gBAppCount++;
00106 }
00107 
00108 
00109 //-------------------------------------------------------------------------
00110 //
00111 // Create the application shell
00112 //
00113 //-------------------------------------------------------------------------
00114 
00115 NS_IMETHODIMP nsAppShell::Create(int* argc, char ** argv)
00116 {
00117   // system wide unique names
00118   // NOTE: this needs to be run from within the main application thread
00119   char        portname[64];
00120   char        semname[64];
00121   PR_snprintf(portname, sizeof(portname), "event%lx", 
00122               (long unsigned) PR_GetCurrentThread());
00123   PR_snprintf(semname, sizeof(semname), "sync%lx", 
00124               (long unsigned) PR_GetCurrentThread());
00125               
00126 #ifdef DEBUG              
00127   printf("nsAppShell::Create portname: %s, semname: %s\n", portname, semname);
00128 #endif
00129   /* 
00130    * Set up the port for communicating. As restarts thru execv may occur
00131    * and ports survive those (with faulty events as result). Combined with the fact
00132    * that plevent.c can setup the port ahead of us we need to take extra
00133    * care that the port is created for this launch, otherwise we need to reopen it
00134    * so that faulty messages gets lost.
00135    *
00136    * We do this by checking if the sem has been created. If it is we can reuse the port (if it exists).
00137    * Otherwise we need to create the sem and the port, deleting any open ports before.
00138    * TODO: The semaphore is no longer needed for syncing, so it's only use is for detecting if the
00139    * port needs to be reopened. This should be replaced, but I'm not sure how -tqh
00140    */
00141   syncsem = my_find_sem(semname);
00142   eventport = find_port(portname);
00143   if(B_ERROR != syncsem) 
00144   {
00145     if(eventport < 0)
00146     {
00147       eventport = create_port(200, portname);
00148     }
00149     return NS_OK;
00150   } 
00151   if(eventport >= 0)
00152   {
00153     delete_port(eventport);
00154   }
00155   eventport = create_port(200, portname);
00156   syncsem = create_sem(0, semname);
00157 
00158   return NS_OK;
00159 }
00160 
00161 //-------------------------------------------------------------------------
00162 //
00163 // Enter a message handler loop
00164 //
00165 //-------------------------------------------------------------------------
00166 
00167 NS_IMETHODIMP nsAppShell::Run()
00168 {
00169   int32               code;
00170   ThreadInterfaceData id;
00171 
00172   NS_ADDREF_THIS();
00173 
00174   set_thread_priority( find_thread(NULL), B_DISPLAY_PRIORITY);
00175 
00176   if (!mEventQueue)
00177     Spinup();
00178 
00179   if (!mEventQueue)
00180     return NS_ERROR_NOT_INITIALIZED;
00181 
00182   while (!is_port_error)
00183   {
00184     RetrieveAllEvents(true);
00185     
00186     while (CountStoredEvents() > 0) {
00187       // get an event of the best priority
00188       EventItem *newitem = (EventItem *) GetNextEvent();
00189       if (!newitem) break;
00190       
00191       code = newitem->code;
00192       id = newitem->ifdata;
00193       
00194       switch(code)
00195       {
00196       case WM_CALLMETHOD :
00197         {
00198           MethodInfo *mInfo = (MethodInfo *)id.data;
00199           mInfo->Invoke();
00200           if(id.waitingThread != 0)
00201           {
00202             resume_thread(id.waitingThread);
00203           }
00204           delete mInfo;
00205         }
00206         break;
00207 
00208       case 'natv' :  // native queue PLEvent
00209         if (mEventQueue)
00210           mEventQueue->ProcessPendingEvents();
00211         break;
00212 
00213       default :
00214 #ifdef DEBUG
00215         printf("nsAppShell::Run - UNKNOWN EVENT\n");
00216 #endif
00217         break;
00218       }
00219 
00220       delete newitem;
00221       newitem = nsnull;
00222       
00223       RetrieveAllEvents(false); // get newer messages (non-block)
00224     }
00225   }
00226 
00227   Spindown();
00228 
00229   Release();
00230 
00231   return NS_OK;
00232 }
00233 
00234 //-------------------------------------------------------------------------
00235 //
00236 // Exit a message handler loop
00237 //
00238 //-------------------------------------------------------------------------
00239 
00240 NS_IMETHODIMP nsAppShell::Exit()
00241 {
00242 #ifdef DEBUG
00243   fprintf(stderr, "nsAppShell::Exit() called\n");
00244 #endif
00245   // interrupt message flow
00246   close_port(eventport);
00247   delete_sem(syncsem);
00248 
00249   return NS_OK;
00250 }
00251 
00252 //-------------------------------------------------------------------------
00253 //
00254 // nsAppShell destructor
00255 //
00256 //-------------------------------------------------------------------------
00257 nsAppShell::~nsAppShell()
00258 {
00259   if(--gBAppCount == 0)
00260   {
00261     if(be_app->Lock())
00262     {
00263       be_app->Quit();
00264     }
00265   }
00266 }
00267 
00268 //-------------------------------------------------------------------------
00269 //
00270 // GetNativeData
00271 //
00272 //-------------------------------------------------------------------------
00273 void* nsAppShell::GetNativeData(PRUint32 aDataType)
00274 {
00275   // To be implemented.
00276   return nsnull;
00277 }
00278 
00279 //-------------------------------------------------------------------------
00280 //
00281 // Spinup - do any preparation necessary for running a message loop
00282 //
00283 //-------------------------------------------------------------------------
00284 NS_IMETHODIMP nsAppShell::Spinup()
00285 {
00286   nsresult   rv = NS_OK;
00287 
00288   // Get the event queue service
00289   nsCOMPtr<nsIEventQueueService> eventQService = do_GetService(kEventQueueServiceCID, &rv);
00290 
00291   if (NS_FAILED(rv)) {
00292     NS_ASSERTION("Could not obtain event queue service", PR_FALSE);
00293     return rv;
00294   }
00295 
00296   //Get the event queue for the thread.
00297   rv = eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQueue));
00298   
00299   // If we got an event queue, use it.
00300   if (mEventQueue)
00301     goto done;
00302 
00303   // otherwise create a new event queue for the thread
00304   rv = eventQService->CreateThreadEventQueue();
00305   if (NS_FAILED(rv)) {
00306     NS_ASSERTION("Could not create the thread event queue", PR_FALSE);
00307     return rv;
00308   }
00309 
00310   // Ask again nicely for the event queue now that we have created one.
00311   rv = eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQueue));
00312 
00313 done:
00314   ListenToEventQueue(mEventQueue, PR_TRUE);
00315   return rv;
00316 }
00317 
00318 //-------------------------------------------------------------------------
00319 //
00320 // Spindown - do any cleanup necessary for finishing a message loop
00321 //
00322 //-------------------------------------------------------------------------
00323 NS_IMETHODIMP nsAppShell::Spindown()
00324 {
00325   if (mEventQueue) {
00326     ListenToEventQueue(mEventQueue, PR_FALSE);
00327     mEventQueue->ProcessPendingEvents();
00328     mEventQueue = nsnull;
00329   }
00330   return NS_OK;
00331 }
00332 
00333 NS_IMETHODIMP nsAppShell::GetNativeEvent(PRBool &aRealEvent, void *&aEvent)
00334 {
00335   aRealEvent = PR_FALSE;
00336   aEvent = 0;
00337 
00338   return NS_OK;
00339 }
00340 
00341 NS_IMETHODIMP nsAppShell::DispatchNativeEvent(PRBool aRealEvent, void *aEvent)
00342 {
00343   // should we check for eventport initialization ?
00344   char  portname[64];
00345   PR_snprintf(portname, sizeof(portname), "event%lx", 
00346               (long unsigned) PR_GetCurrentThread());
00347 
00348   if((eventport = find_port(portname)) < 0) 
00349   {
00350     // not initialized
00351 #ifdef DEBUG
00352     printf("nsAppShell::DispatchNativeEvent() was called before init\n");
00353 #endif
00354     fflush(stdout);
00355     return NS_ERROR_FAILURE;
00356   }
00357 
00358   int32 code;
00359   ThreadInterfaceData id;
00360   id.data = 0;
00361   id.waitingThread = 0;
00362   bool gotMessage = false;
00363 
00364   do 
00365   {
00366       if (CountStoredEvents() == 0)
00367         RetrieveAllEvents(true); // queue is empty. block until new message comes.
00368       
00369       EventItem *newitem = (EventItem *) GetNextEvent();
00370       if (!newitem) continue;
00371       
00372       code = newitem->code;
00373       id = newitem->ifdata;
00374 
00375       switch(code) 
00376       {
00377         case WM_CALLMETHOD :
00378           {
00379             MethodInfo *mInfo = (MethodInfo *)id.data;
00380             mInfo->Invoke();
00381             if(id.waitingThread != 0)
00382             {
00383               resume_thread(id.waitingThread);
00384             }
00385             delete mInfo;
00386             gotMessage = PR_TRUE;
00387           }
00388           break;
00389         
00390         case 'natv' :       // native queue PLEvent
00391           {
00392             if (mEventQueue)
00393               mEventQueue->ProcessPendingEvents();
00394             gotMessage = PR_TRUE;
00395           }
00396           break;
00397           
00398         default :
00399 #ifdef DEBUG
00400           printf("nsAppShell::Run - UNKNOWN EVENT\n");
00401 #endif
00402           break;
00403       }
00404 
00405       delete newitem;
00406       newitem = nsnull;
00407 
00408   } while (!gotMessage);
00409 
00410   return NS_OK;
00411 }
00412 
00413 NS_IMETHODIMP nsAppShell::ListenToEventQueue(nsIEventQueue *aQueue, PRBool aListen)
00414 {
00415   // do nothing
00416   return NS_OK;
00417 }
00418 
00419 // count all stored events 
00420 int nsAppShell::CountStoredEvents()
00421 {
00422   int count = 0;
00423   for (int i=0 ; i < PRIORITY_LEVELS ; i++)
00424     count += events[i].CountItems();
00425   
00426   return count;
00427 }
00428 
00429 // get an event of the best priority
00430 void *nsAppShell::GetNextEvent()
00431 {
00432   void *newitem = nsnull;
00433   for (int i=0 ; i < PRIORITY_LEVELS ; i++) {
00434     if (!events[i].IsEmpty()) {
00435       newitem = events[i].RemoveItem((long int)0);
00436       break;
00437     }
00438   }
00439   return newitem;
00440 }
00441 
00442 // get all the messages on the port and dispatch them to 
00443 // several queues by priority.
00444 void nsAppShell::RetrieveAllEvents(bool blockable)
00445 {
00446   if (is_port_error) return;
00447   
00448   bool is_first_loop = true;
00449   while(true)
00450   {
00451     EventItem *newitem = new EventItem;
00452     if ( !newitem ) break;
00453 
00454     newitem->code = 0;
00455     newitem->ifdata.data = nsnull;
00456     newitem->ifdata.waitingThread = 0;
00457 
00458     // only block on read_port when 
00459     //   blockable == true
00460     //   and
00461     //   this is the first loop
00462     // otherwise, return immediately.
00463     if ( (!is_first_loop || !blockable) && port_count(eventport) <= 0 ) {
00464       delete newitem;
00465       break;
00466     }
00467     is_first_loop = false;
00468     if ( read_port(eventport, &newitem->code, &newitem->ifdata, sizeof(newitem->ifdata)) < 0 ) {
00469       delete newitem;
00470       is_port_error = true;
00471       return;
00472     }
00473     // synchronous events should be processed quickly (?)
00474     if (newitem->ifdata.waitingThread != 0) {
00475       events[PRIORITY_TOP].AddItem(newitem);
00476     } else {
00477       switch(newitem->code)
00478       {
00479       case WM_CALLMETHOD :
00480         {
00481           MethodInfo *mInfo = (MethodInfo *)newitem->ifdata.data;
00482           switch( mInfo->methodId ) {
00483           case nsSwitchToUIThread::ONKEY :
00484             events[PRIORITY_SECOND].AddItem(newitem);
00485             break;
00486           case nsSwitchToUIThread::ONMOUSE:
00487             ConsumeRedundantMouseMoveEvent(mInfo);
00488             events[PRIORITY_THIRD].AddItem(newitem);
00489             break;
00490           case nsSwitchToUIThread::ONWHEEL :
00491           case nsSwitchToUIThread::BTNCLICK :
00492             events[PRIORITY_THIRD].AddItem(newitem);
00493             break;
00494           default:
00495             events[PRIORITY_NORMAL].AddItem(newitem);
00496             break;
00497           }
00498         }
00499         break;
00500             
00501       case 'natv' :  // native queue PLEvent
00502         events[PRIORITY_LOW].AddItem(newitem);
00503         break;
00504       }
00505     }
00506   }
00507   return;
00508 }
00509 
00510 // detect sequential NS_MOUSE_MOVE event and delete older one,
00511 // for the purpose of performance
00512 void nsAppShell::ConsumeRedundantMouseMoveEvent(MethodInfo *pNewEventMInfo)
00513 {
00514   if (pNewEventMInfo->args[0] != NS_MOUSE_MOVE) return;
00515 
00516   nsISupports *widget0 = pNewEventMInfo->widget;
00517   nsSwitchToUIThread *target0 = pNewEventMInfo->target;
00518   
00519   int count = events[PRIORITY_THIRD].CountItems();
00520   for (int i=count-1 ; i >= 0 ; i --) {
00521     EventItem *previtem = (EventItem *)events[PRIORITY_THIRD].ItemAt(i);
00522     if (!previtem) continue;
00523     MethodInfo *mInfoPrev = (MethodInfo *)previtem->ifdata.data;
00524     if (!mInfoPrev
00525       || mInfoPrev->widget != widget0
00526       || mInfoPrev->target != target0) continue;
00527     // if other mouse event was found, then no sequential.
00528     if (mInfoPrev->args[0] != NS_MOUSE_MOVE) break;
00529     // check if other conditions are the same
00530     if (mInfoPrev->args[3] == pNewEventMInfo->args[3]
00531       && mInfoPrev->args[4] == pNewEventMInfo->args[4]) {
00532       // sequential mouse move found!
00533       events[PRIORITY_THIRD].RemoveItem(previtem);
00534       delete mInfoPrev;
00535       //if it's a synchronized call also wake up thread.
00536       if(previtem->ifdata.waitingThread != 0)
00537         resume_thread(previtem->ifdata.waitingThread);
00538       delete previtem;
00539       break;
00540     }
00541   }
00542   return;
00543 }
00544