All pastes #294426 Raw Edit

Untitled

public text v1 · immutable
#294426 ·published 2006-12-28 13:42 UTC
rendered paste body
/*
* ====================================================================
*  e32module.cpp  
*  
*  Basic EPOC facilities for Python
*
*  Implements currently (23.11.2004) following Python classes / methods:
*
*  Ao_lock -- Symbian active object -based synchronization
*    Ao_lock()
*    wait()
*    signal()
*
*  Ao_timer -- Symbian active object -based timer
*    Ao_timer()
*    after()
*    cancel()
*
*  ao_yield()
*
*  ao_sleep(float [,callable])
*
*  callable ao_callgate(callable)
*
*  file_copy(unicode_string OR string, unicode_string OR string)
*
*  start_server(unicode_string OR string)
*
*  start_exe(unicode_string OR string, unicode_string OR string [,int])
*
*  [unicode_string] drive_list()
*
*  bool is_ui_thread()
*
*     
* Copyright (c) 2005-2006 Nokia Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/

#include <e32std.h>
#include <e32uid.h>   // e32_uidcrc_app()
#include <eikapp.h>
#include <f32file.h>
#include <bautils.h>  // e32_file_copy()
#include <apmstd.h>
#include <hal.h>      // e32_clock()
#include "Python.h"
#include "symbian_python_ext_util.h"
#include "CSPyInterpreter.h" // e32_stdo(), e32_mem_info()

/*
 *
 * Utilities for e32.start_server() and e32.start_exe()
 *
 */

class CE32ProcessWait : public CActive
{
public:
  CE32ProcessWait():CActive(EPriorityStandard) {
    CActiveScheduler::Add(this);
  }
#if defined(__WINS__) && !defined(EKA2)  
  TInt Wait(RThread& aProcess) {
#else
  TInt Wait(RProcess& aProcess) {
#endif
    aProcess.Logon(iStatus);
    aProcess.Resume();
    SetActive();
#ifdef HAVE_ACTIVESCHEDULERWAIT   
    iWait.Start();
#else
    CActiveScheduler::Start();
#endif
    return iStatus.Int();
  }

  private:
  void DoCancel() {;}
  void RunL() {
#ifdef HAVE_ACTIVESCHEDULERWAIT
    iWait.AsyncStop();
#else
    CActiveScheduler::Stop();
#endif
  }
#ifdef HAVE_ACTIVESCHEDULERWAIT
  CActiveSchedulerWait iWait;
#endif
};

static TInt ProcessLaunch(const TDesC& aFileName, const TDesC& aCommand,
                     TInt aWaitFlag=0)
{
  TInt error;
  Py_BEGIN_ALLOW_THREADS
#if defined(__WINS__) && !defined(EKA2)
  RThread proc;
  RLibrary lib;
  HBufC* pcommand = aCommand.Alloc();
  error = lib.Load(aFileName);
  if (error == KErrNone) {
    TThreadFunction func = (TThreadFunction)(lib.Lookup(1));
    error = proc.Create(_L(""), func, 0x1000, (TAny*) pcommand, &lib,
                        RThread().Heap(), 0x1000, 0x100000, EOwnerProcess);
    lib.Close();
  }
  else
    delete pcommand;
#else
  RProcess proc;
  error = proc.Create(aFileName, aCommand);
#endif
  if (error == KErrNone)
    if (aWaitFlag) {
      CE32ProcessWait* w = new CE32ProcessWait();
      if (w) {
        error = w->Wait(proc);
        delete w;
      }
      else
        error = KErrNoMemory;
    }
    else
      proc.Resume();
  proc.Close();
  Py_END_ALLOW_THREADS
  return error;
}

/*
 *
 * Implementation of e32.start_server()
 *
 */
extern "C" PyObject *
e32_start_server(PyObject* /*self*/, PyObject* args)
{
  PyObject* it;

  if (!PyArg_ParseTuple(args, "O", &it))
    return NULL;

  PyObject* fn = PyUnicode_FromObject(it);
  if (!fn)
    return NULL;

  TPtrC name(PyUnicode_AsUnicode(fn), PyUnicode_GetSize(fn));
  TParse p;
  p.Set(name, NULL, NULL);
  
  if (!(p.Ext().CompareF(_L(".py")) == 0)) {
    Py_DECREF(fn);
    PyErr_SetString(PyExc_TypeError, "Python script name expected");
    return NULL;
  }
  
  TInt error;
  error =
#if defined(__WINS__) && !defined(EKA2)
    ProcessLaunch(_L("\\system\\programs\\Python_launcher.dll"), name);
#else
    ProcessLaunch(_L("python_launcher.exe"), name);
#endif

  Py_DECREF(fn);
  
  RETURN_ERROR_OR_PYNONE(error);
}


/*
 *
 * Implementation of e32.start_exe()
 *
 */

extern "C" PyObject *
e32_start_exe(PyObject* /*self*/, PyObject* args)
{
  PyObject *it0, *it1;
  int wait_flag = 0;

  if (!PyArg_ParseTuple(args, "OO|i", &it0, &it1, &wait_flag))
    return NULL;

  PyObject* n = PyUnicode_FromObject(it0);
  if (!n)
    return NULL;
  
  TPtrC name(PyUnicode_AsUnicode(n), PyUnicode_GetSize(n));
  TParse p;
  p.Set(name, NULL, NULL);
  
#if defined(__WINS__) && !defined(EKA2)
  if (!(p.Ext().CompareF(_L(".dll")) == 0)) {
#else
  if (!(p.Ext().CompareF(_L(".exe")) == 0)) {
#endif
    Py_DECREF(n);
    PyErr_SetString(PyExc_TypeError, "Executable expected");
    return NULL;
  }
  
  PyObject* a = PyUnicode_FromObject(it1);
  if (!a) {
    Py_DECREF(n);
    return NULL;
  }

  TInt error = ProcessLaunch(name,
                             TPtrC(PyUnicode_AsUnicode(a),
                                   PyUnicode_GetSize(a)),
                             wait_flag);

  Py_DECREF(n);
  Py_DECREF(a);
  
  if (wait_flag && (error >= 0))
    return Py_BuildValue("i", error);
  else
    RETURN_ERROR_OR_PYNONE(error);
}

/*
 *
 * Implementation of e32.drive_list()
 *
 */

extern "C" PyObject *
e32_drive_list(PyObject* /*self*/)
{
  TInt error;
  RFs rfs;
  
  if ((error = rfs.Connect()) != KErrNone)
    return SPyErr_SetFromSymbianOSErr(error);

  PyObject* r;
  TDriveList l;

  error = rfs.DriveList(l);
  
  if (error == KErrNone) {
    if (r = PyList_New(0)) {
      for (int i = 0; i < KMaxDrives; i++) {
        if (l[i]) {
          char d[2];
          d[0] = 'A'+i; d[1] = ':';
          
          PyObject* v = PyUnicode_Decode(d, 2, NULL, NULL); 
          if ((v == NULL) || (PyList_Append(r, v) != 0)) {
            Py_XDECREF(v);
            Py_DECREF(r);
            r = NULL;
            break;
          }
          Py_DECREF(v);
        }
      }
    }
  }
  else
    r = SPyErr_SetFromSymbianOSErr(error);

  rfs.Close();

  return r;
}

/*
 *
 * Implementation of e32.file_copy()
 *
 */

extern "C" PyObject *
e32_file_copy(PyObject* /*self*/, PyObject* args)
{
  PyObject *it0, *it1;

  if (!PyArg_ParseTuple(args, "OO", &it0, &it1))
    return NULL;

  PyObject *t, *s;

  if (!((t = PyUnicode_FromObject(it0)) &&
        (s = PyUnicode_FromObject(it1)))) {
    Py_XDECREF(t);
    return NULL;
  }

  TPtrC target(PyUnicode_AsUnicode(t), PyUnicode_GetSize(t));
  TPtrC source(PyUnicode_AsUnicode(s), PyUnicode_GetSize(s));
  
  TInt error;
  RFs rfs;
  
  if ((error = rfs.Connect()) == KErrNone) {
    error = BaflUtils::CopyFile(rfs, source, target);
    rfs.Close();
  }

  Py_DECREF(t); Py_DECREF(s);

  RETURN_ERROR_OR_PYNONE(error);
}

/*
 *
 * Implementation of e32.Ao_lock
 *
 */

#define Ao_lock_type ((PYTHON_GLOBALS->tobj).t_Ao)

#ifndef EKA2
class Ao_lock : public CActive {
#else
NONSHARABLE_CLASS(Ao_lock) : public CActive {
#endif
public:
  Ao_lock();
  TInt Signal(TUint aTid);
  void Wait();
private:
  void RunL();
  void DoCancel() {;}
  TRequestStatus* iPst;
#ifdef HAVE_ACTIVESCHEDULERWAIT
  CActiveSchedulerWait iWait;
#endif
};

Ao_lock::Ao_lock():CActive(0)
{
  iStatus = KErrCancel;
  iPst = &iStatus;
  CActiveScheduler::Add(this);
}

void Ao_lock::Wait()
{
  if (iStatus != KErrNone) {
    iStatus = KRequestPending;
    iPst = &iStatus;
    SetActive();

    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_ACTIVESCHEDULERWAIT   
    iWait.Start();
#else
    CActiveScheduler::Start();
#endif
    Py_END_ALLOW_THREADS
  }
  iStatus = KErrCancel;
  return;
}

TInt Ao_lock::Signal(TUint aTid)
{
  TInt error = KErrNone;

  if (iStatus != KRequestPending) {
    iStatus = KErrNone;
    return error;
  }
  RThread t;
  error = t.Open(aTid);
  if (error == KErrNone) {
    //iStatus = KErrNone; // Doing this will panic the 3.0 emulator with E32USER-CBase 46 "Stray signal".
    t.RequestComplete(iPst, 0);
    t.Close();
  }
  return error;
}

void Ao_lock::RunL()
{
#ifdef HAVE_ACTIVESCHEDULERWAIT
  iWait.AsyncStop();
#else
  CActiveScheduler::Stop();
#endif
}

struct Ao_lock_object {
  PyObject_VAR_HEAD
  Ao_lock* ob_data;
  unsigned int ob_tid;
};

extern "C" PyObject *
new_e32_ao_object(PyObject* /*self*/)
{
  if (!CActiveScheduler::Current()) {
    PyErr_SetString(PyExc_AssertionError, "no ao scheduler");
    return NULL;
  }

  Ao_lock_object *op = PyObject_New(Ao_lock_object, &Ao_lock_type);
  if (op == NULL)
    return PyErr_NoMemory();

  op->ob_data = new Ao_lock();
  if (op->ob_data == NULL) {
    PyObject_Del(op);
    return PyErr_NoMemory();
  }
  op->ob_tid = RThread().Id();
  return (PyObject *) op;
}

extern "C" PyObject *
ao_wait(Ao_lock_object *self, PyObject* /*args*/)
{
  if ((TUint)RThread().Id() != self->ob_tid) {
    PyErr_SetString(PyExc_AssertionError,
                    "Ao_lock.wait must be called from lock creator thread");
    return NULL;
  }
  if (self->ob_data->iStatus == KRequestPending) {
    PyErr_SetString(PyExc_AssertionError, "wait() called on Ao_lock while another wait() on the same lock is in progress");
    return NULL;
  }
  self->ob_data->Wait(); 
  Py_INCREF(Py_None);
  return Py_None;
}

extern "C" PyObject *
ao_signal(Ao_lock_object *self, PyObject* /*args*/)
{
  TInt error = self->ob_data->Signal(self->ob_tid);
  RETURN_ERROR_OR_PYNONE(error);
}

extern "C" {

  static void
  ao_dealloc(Ao_lock_object *op)
  {
    if ((TUint)RThread().Id() == op->ob_tid) {
      delete op->ob_data;
      op->ob_data = NULL;
    }
    PyObject_Del(op);
  }
  
  static const PyMethodDef ao_methods[] = {
    {"wait", (PyCFunction)ao_wait, METH_NOARGS},
    {"signal", (PyCFunction)ao_signal, METH_NOARGS},
    {NULL,              NULL}           /* sentinel */
  };

  static PyObject *
  ao_getattr(Ao_lock_object *p, char *name)
  {
    return Py_FindMethod((PyMethodDef*)ao_methods,
                         (PyObject *)p, name);
  }

  static const PyTypeObject c_Ao_lock_type = {
    PyObject_HEAD_INIT(NULL)
    0,
    "e32.Ao_lock",
    sizeof(Ao_lock_object),
    0,
    /* methods */
    (destructor)ao_dealloc,             /* tp_dealloc */
    0,                                  /* tp_print */
    (getattrfunc)ao_getattr,            /* tp_getattr */
    0,                                  /* tp_setattr */
    0,                                  /* tp_compare */
    0,                                  /* tp_repr */
    0,                                  /* tp_as _number*/
    0,                                  /* tp_as _sequence*/
    0,                                  /* tp_as _mapping*/
    0,                                  /* tp_hash */
  };
} /* extern "C" */

/*
 *
 * Implementation of e32.ao_yield
 *
 */

class CE32AoYield : public CActive
{
 public:
  CE32AoYield():CActive(EPriorityStandard) {
    CActiveScheduler::Add(this);
  }
  
  void DoYield() {
    iStatus = KRequestPending;
    SetActive();
    //iStatus = KErrNone; // Doing this will panic the 3.0 emulator with E32USER-CBase 46 "Stray signal".
    TRequestStatus* pstatus = &iStatus;
    RThread().RequestComplete(pstatus, 0);
#ifdef HAVE_ACTIVESCHEDULERWAIT   
    iWait.Start();
#else
    CActiveScheduler::Start();
#endif
  }

 private:
  void DoCancel() {;}
  void RunL() {
#ifdef HAVE_ACTIVESCHEDULERWAIT
    iWait.AsyncStop();
#else
    CActiveScheduler::Stop();
#endif
  }

#ifdef HAVE_ACTIVESCHEDULERWAIT
  CActiveSchedulerWait iWait;
#endif
};

extern "C" PyObject *
e32_ao_yield(PyObject* /*self*/)
{
  CE32AoYield* y = new CE32AoYield();
  if (!y)
    return PyErr_NoMemory();
  
  Py_BEGIN_ALLOW_THREADS
  y->DoYield();
  delete y;
  Py_END_ALLOW_THREADS

  Py_INCREF(Py_None);
  return Py_None;
}

/*
 *
 * Implementation of e32.ao_sleep
 *
 */

class CE32AoSleep : public CActive
{
 public:
  CE32AoSleep(PyObject* aCb=0):CActive(EPriorityStandard) {
    iCb = aCb;
    Py_XINCREF(iCb);
    //iMaxTime(in seconds) is max integer where (2147*10exp6< 2exp31)
    //This restriction is needed to avoid overflow at
    //RTimer.After(TTimeIntervalMicroSeconds32) 
    iMaxTime = 2147;
  }
  ~CE32AoSleep() {
    Cancel();
    iTimer.Close();
    Py_XDECREF(iCb);
  }

  TInt Construct() {
    TInt error = iTimer.CreateLocal();
    if (error == KErrNone)
      CActiveScheduler::Add(this);
    return error;
  }
  void DoSleep(TReal aDelay /*time in seconds*/) {
    TInt timeMultiplier = aDelay / iMaxTime;  
    TReal timeLeft = aDelay - (iMaxTime * timeMultiplier);
    if (timeMultiplier > 0) {
      for (TInt i = 0; i< timeMultiplier; i++) 
      {
       StartTimer(iMaxTime); 
      }
    }
    if (timeLeft > 0) {
      StartTimer(timeLeft);
    } 
  }

 private:
  void DoCancel() {
    iTimer.Cancel();
  }
  void RunL() {
    if (!iCb) {
#ifdef HAVE_ACTIVESCHEDULERWAIT
      iWait.AsyncStop();
#else
      CActiveScheduler::Stop();
#endif
    }
    else {
      PyEval_RestoreThread(PYTHON_TLS->thread_state);
      PyObject* tmp_r = NULL;
      tmp_r = PyEval_CallObject(iCb, NULL);
      Py_XDECREF(tmp_r);
      if (PyErr_Occurred())
	      PyErr_Print();
      PyEval_SaveThread();
    }
  }
  void StartTimer(TReal aDelay) {
    TTimeIntervalMicroSeconds32 delay;
#ifndef EKA2 
    delay = TTimeIntervalMicroSeconds32(TInt64(aDelay*1000*1000).GetTInt());
#else
    delay = TTimeIntervalMicroSeconds32(TInt64(aDelay*1000*1000));
#endif
    iTimer.After(iStatus, delay);
    SetActive();
    if (!iCb) {
      Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_ACTIVESCHEDULERWAIT   
      iWait.Start();
#else
      CActiveScheduler::Start();
#endif
      Py_END_ALLOW_THREADS
    }
  }   

  RTimer iTimer;
  TInt iMaxTime;
#ifdef HAVE_ACTIVESCHEDULERWAIT
  CActiveSchedulerWait iWait;
#endif
  PyObject* iCb;
};


extern "C" PyObject *
e32_ao_sleep(PyObject* /*self*/, PyObject* args)
{
  TReal d;
  PyObject* c=NULL;

  if (!PyArg_ParseTuple(args, "d|O", &d, &c))
    return NULL;

  if (c && !PyCallable_Check(c)) {
    PyErr_SetString(PyExc_TypeError, "callable expected for 2nd argument");
    return NULL;
  }

  if (d < 0) {
    PyErr_SetString(PyExc_RuntimeError, "negative number not allowed");
    return NULL;
  }

  CE32AoSleep* s = new CE32AoSleep(c);
  if (!s)
    return PyErr_NoMemory();

  TInt error = s->Construct();
  if (error != KErrNone) {
    delete s;
    return SPyErr_SetFromSymbianOSErr(error);
  }

  s->DoSleep(d);
  delete s;
  Py_INCREF(Py_None);
  return Py_None;
}


/*
 *
 * Implementation of e32.Ao_timer
 *
 */

#define Ao_timer_type (PYTHON_GLOBALS->t_Ao_timer)

#ifndef EKA2
class Ao_timer : public CActive {
#else
NONSHARABLE_CLASS(Ao_timer) : public CActive {
#endif
public:
  Ao_timer();
  void After(TTimeIntervalMicroSeconds32 aSleep, PyObject* aCb);
  TInt Construct();
  ~Ao_timer();
private:
  void RunL();
  void DoCancel();
  RTimer iTimer;
  PyObject* iCb;
#ifdef HAVE_ACTIVESCHEDULERWAIT
  CActiveSchedulerWait iWait;
#endif
};

Ao_timer::Ao_timer():CActive(EPriorityStandard) {;}

void Ao_timer::After(TTimeIntervalMicroSeconds32 aDelay, PyObject* aCb=0)
{
  iCb = aCb;
  Py_XINCREF(iCb);
  iTimer.After(iStatus, aDelay);
  SetActive();
  if (!iCb) {
    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_ACTIVESCHEDULERWAIT   
    iWait.Start();
#else
    CActiveScheduler::Start();
#endif
    Py_END_ALLOW_THREADS
  }
}

TInt Ao_timer::Construct() 
{
  TInt error = iTimer.CreateLocal();
  if (error == KErrNone)
    CActiveScheduler::Add(this);
  return error;
}

Ao_timer::~Ao_timer()
{
  Cancel();
  iTimer.Close();
  Py_XDECREF(iCb);  
}

void Ao_timer::RunL()
{
    if (!iCb) {
#ifdef HAVE_ACTIVESCHEDULERWAIT
      iWait.AsyncStop();
#else
      CActiveScheduler::Stop();
#endif
    }
    else {
    PyEval_RestoreThread(PYTHON_TLS->thread_state);
    PyObject* tmp_r = NULL;
    tmp_r = PyEval_CallObject(iCb, NULL);
    Py_XDECREF(tmp_r);
    if (PyErr_Occurred())
	    PyErr_Print();
    PyEval_SaveThread();
  }
}

void Ao_timer::DoCancel() 
{
  iTimer.Cancel();
}

struct Ao_timer_object {
  PyObject_VAR_HEAD
  Ao_timer* ob_data;
};

extern "C" PyObject *
new_e32_ao_timer_object(PyObject* /*self*/, PyObject /**args*/)
{
  if (!CActiveScheduler::Current()) {
    PyErr_SetString(PyExc_AssertionError, "no ao scheduler");
    return NULL;
  }

  Ao_timer_object *op = PyObject_New(Ao_timer_object, &Ao_timer_type);
  if (op == NULL)
    return PyErr_NoMemory();

  op->ob_data = new Ao_timer();
  if (op->ob_data == NULL) {
    PyObject_Del(op);
    return PyErr_NoMemory();
  }

  TInt error = op->ob_data->Construct();
  if (error != KErrNone) {
    PyObject_Del(op);
    return SPyErr_SetFromSymbianOSErr(error);
  }  
  
  return (PyObject *) op;
}

extern "C" PyObject *
ao_timer_after(Ao_timer_object *self, PyObject* args)
{
  TReal d;
  PyObject* c=NULL;

  if (!PyArg_ParseTuple(args, "d|O", &d, &c))
    return NULL;

  if (c && !PyCallable_Check(c)) {
    PyErr_SetString(PyExc_TypeError, "callable expected for 2nd argument");
    return NULL;
  }
  
  if(self->ob_data->IsActive()) {
    PyErr_SetString(PyExc_RuntimeError, "Timer pending - cancel first");
    return NULL;  
  }
  
  if (d < 0) {
    PyErr_SetString(PyExc_RuntimeError, "negative number not allowed");
    return NULL;
  }
  #ifndef EKA2 
  self->ob_data->After(TTimeIntervalMicroSeconds32(TInt64(d*1000*1000).GetTInt()), c);
  #else
  self->ob_data->After(TTimeIntervalMicroSeconds32(TInt64(d*1000*1000)), c);
  #endif /*EKA2*/ 
  Py_INCREF(Py_None);
  return Py_None;
}

extern "C" PyObject *
ao_timer_cancel(Ao_timer_object *self, PyObject* /*args*/)
{
  self->ob_data->Cancel();
  
  Py_INCREF(Py_None);
  return Py_None;
}

extern "C" {

  static void
  ao_timer_dealloc(Ao_timer_object *op)
  {
    delete op->ob_data;
    op->ob_data = NULL;

    PyObject_Del(op);
  }
  
  static const PyMethodDef ao_timer_methods[] = {
    {"after", (PyCFunction)ao_timer_after, METH_VARARGS},
    {"cancel", (PyCFunction)ao_timer_cancel, METH_NOARGS},
    {NULL,              NULL}           /* sentinel */
  };

  static PyObject *
  ao_timer_getattr(Ao_timer_object *p, char *name)
  {
    return Py_FindMethod((PyMethodDef*)ao_timer_methods,
                         (PyObject *)p, name);
  }

  static const PyTypeObject c_Ao_timer_type = {
    PyObject_HEAD_INIT(NULL)
    0,
    "e32.Ao_timer",
    sizeof(Ao_timer_object),
    0,
    /* methods */
    (destructor)ao_timer_dealloc,       /* tp_dealloc */
    0,                                  /* tp_print */
    (getattrfunc)ao_timer_getattr,      /* tp_getattr */
    0,                                  /* tp_setattr */
    0,                                  /* tp_compare */
    0,                                  /* tp_repr */
    0,                                  /* tp_as _number*/
    0,                                  /* tp_as _sequence*/
    0,                                  /* tp_as _mapping*/
    0,                                  /* tp_hash */
  };
} /* extern "C" */


/*
 *
 * Implementation of e32.ao_callgate
 *
 */

#define Ao_callgate_type ((PYTHON_GLOBALS->tobj).t_Ao_callgate)


struct Ao_callgate_call_item {
  PyObject* arg;
  Ao_callgate_call_item* next;
};

#ifndef EKA2
class Ao_callgate;
#else
NONSHARABLE_CLASS(Ao_callgate);
#endif

struct Ao_callgate_object {
  PyObject_VAR_HEAD
  Ao_callgate* ob_data;
  unsigned int ob_tid;
  Ao_callgate_call_item ob_args;
};

#ifndef EKA2
class Ao_callgate : public CActive {
#else
NONSHARABLE_CLASS(Ao_callgate) : public CActive {
#endif
public:
  Ao_callgate(Ao_callgate_object* aOb, PyObject* aCb, TInt aTid);
  ~Ao_callgate() {
    Cancel();
    Py_DECREF(iCb);
  }
  TInt Signal(TInt aStatus=KErrNone);
private:
  void RunL();
  void DoCancel() {
    Signal(KErrCancel);
  }
  TRequestStatus* iPst;
  PyObject* iCb;
  Ao_callgate_object* iOb;
  TInt iTid;
};

Ao_callgate::Ao_callgate(Ao_callgate_object* aOb, PyObject* aCb, TInt aTid):
  CActive(0),iTid(aTid),iOb(aOb)
{
  iCb = aCb;
  Py_INCREF(iCb);
  CActiveScheduler::Add(this);
  iStatus = KRequestPending;
  iPst = &iStatus;
  SetActive();
}

TInt Ao_callgate::Signal(TInt aReason)
{
  if (iStatus != KRequestPending)
    return KErrNone;

  RThread t;
  TInt error = t.Open(iTid);
  if (error == KErrNone) {
    t.RequestComplete(iPst, aReason);
    t.Close();
  }
  return error;
}

void Ao_callgate::RunL()
{
  PyEval_RestoreThread(PYTHON_TLS->thread_state);
  Ao_callgate_call_item* p = iOb->ob_args.next;
  iOb->ob_args.next = NULL;
  while (p) {
    PyObject* tmp_r = NULL;
    tmp_r = PyEval_CallObject(iCb, p->arg);
    Py_XDECREF(tmp_r);
    if (PyErr_Occurred())
      PyErr_Print();
    Ao_callgate_call_item* tmp = p->next;
    Py_XDECREF(p->arg);
    PyMem_Free(p);
    p = tmp;
  }
  PyEval_SaveThread();
  iStatus = KRequestPending;
  iPst = &iStatus;
  SetActive();
}

extern "C" PyObject *
e32_ao_callgate(PyObject* /*self*/, PyObject* args)
{
  if (!CActiveScheduler::Current()) {
    PyErr_SetString(PyExc_AssertionError, "no ao scheduler");
    return NULL;
  }

  PyObject* c;
  if (!PyArg_ParseTuple(args, "O", &c) || !PyCallable_Check(c))
    return NULL;

  Ao_callgate_object *op = PyObject_New(Ao_callgate_object,
                                        &Ao_callgate_type);
  if (op == NULL)
    return PyErr_NoMemory();

  op->ob_args.arg = NULL;
  op->ob_args.next = NULL;
  op->ob_tid = RThread().Id();
  if (!(op->ob_data = new Ao_callgate(op, c, op->ob_tid))) {
    PyObject_Del(op);
    return PyErr_NoMemory();
  }
  return (PyObject *) op;
}

extern "C" PyObject *
ao_cg_call(PyObject* self, PyObject* arg, PyObject* /*kw*/)
{
  Ao_callgate_object *op = (Ao_callgate_object*)self;
  Ao_callgate_call_item* n =
    (Ao_callgate_call_item*)PyMem_Malloc(sizeof(Ao_callgate_call_item));
  if (!n)
    return PyErr_NoMemory();
  n->arg = arg;
  Py_XINCREF(arg);
  n->next = NULL;

  Ao_callgate_call_item* p = &(op->ob_args);
  while (p->next) p = p->next;
  p->next = n;
  
  TInt error = op->ob_data->Signal();
  if (error != KErrNone) {
    p->next = NULL;
    Py_XDECREF(n->arg);
    PyMem_Free(n);
  }
  RETURN_ERROR_OR_PYNONE(error);
}

extern "C" {

  static void
  ao_cg_dealloc(Ao_callgate_object *op)
  {
    if ((TUint)RThread().Id() == op->ob_tid) {
      delete op->ob_data;
      op->ob_data = NULL;
    }
    Ao_callgate_call_item* p = op->ob_args.next;
    while (p) {
      Ao_callgate_call_item* tmp = p->next;
      Py_XDECREF(p->arg);
      PyMem_Free(p);
      p = tmp;
    }
    PyObject_Del(op);
  }

  static const PyTypeObject c_Ao_callgate_type = {
    PyObject_HEAD_INIT(NULL)
    0,
    "e32.Ao_callgate",
    sizeof(Ao_callgate_object),
    0,
    /* methods */
    (destructor)ao_cg_dealloc,          /* tp_dealloc */
    0,                                  /* tp_print */
    0,                                  /* tp_getattr */
    0,                                  /* tp_setattr */
    0,                                  /* tp_compare */
    0,                                  /* tp_repr */
    0,                                  /* tp_as _number*/
    0,                                  /* tp_as _sequence*/
    0,                                  /* tp_as _mapping*/
    0,                                  /* tp_hash */
    ao_cg_call,                         /* tp_call */
  };
} /* extern "C" */

/*
 *
 * Implementation of e32._as_level
 *
 */

class CMyAccessor : public CActiveScheduler
{
public:
  TInt MyLevel() {return Level();}
};

extern "C" PyObject *
e32__as_level(PyObject* /*self*/, PyObject* /*args*/)
{
  TInt l =
    STATIC_CAST(CMyAccessor*, CActiveScheduler::Current())->MyLevel();
  return Py_BuildValue("i", l);
}

/*
 * This does not work on target (!?)
 *
 * extern "C" double
 * e32_clock()
 * {
 *   TTimeIntervalMicroSeconds cpu_time(0.0);
 *
 *   RThread().GetCpuTime(cpu_time);
 *
 *   return ((cpu_time.Int64().GetTReal()) / (1000.0 * 1000.0));
 * }
 */

extern "C" double
e32_clock()
{
  TInt period = 1000*1000;
  
  HAL::Get(HALData::ESystemTickPeriod, period);
  
  return (((double)(((double)User::TickCount())*
                    (((double)period)/1000.0)))/1000.0);
}

extern "C" double
e32_UTC_offset()
{
#ifndef EKA2
  TLocale loc;
  return TInt64(loc.UniversalTimeOffset().Int()).GetTReal();
#else
  TLocale loc;
  return TReal64(loc.UniversalTimeOffset().Int());
#endif /*EKA2*/
}


extern "C" int
e32_check_stack()
{
#ifndef EKA2
  TInt heap_sz, stack_sz;
  
  if (RThread().GetRamSizes(heap_sz, stack_sz) == KErrNone) {
    if (((TUint)&heap_sz-(TUint)((RThread().Heap())->Base()-stack_sz)) > 0x800)
      return 0;
  }
  else
    return 0;
  
  return (-1);
#else
  return 0; // XXX Hard coded stack ok
#endif /*EKA2*/
}

/*
 *
 * Implementation of e32.is_ui_thread
 *
 */

static TBool is_main_thread()
{
  return ((PYTHON_GLOBALS->main_thread == 0) ||
          (PYTHON_GLOBALS->main_thread == PyThread_get_thread_ident()));
}

extern "C" PyObject *
e32_is_ui_thread(PyObject* /*self*/, PyObject* /*args*/)
{
  PyObject* rval = (is_main_thread() ? Py_True : Py_False);
  Py_INCREF(rval);
  return rval;
}

/*
 *
 * Implementation of e32.in_emulator
 *
 */

extern "C" PyObject *
e32_in_emulator(PyObject* /*self*/, PyObject* /*args*/)
{
#ifdef __WINS__
  PyObject* rval = Py_True;
#else
  PyObject* rval = Py_False;
#endif
  Py_INCREF(rval);
  return rval;
}

/*
 *
 * Implementation of e32.set_home_time
 *
 */

extern "C" PyObject *
e32_set_home_time(PyObject* /*self*/, PyObject* args)
{
  TInt error = KErrNone;
  TReal time = 0;
  if (!PyArg_ParseTuple(args, "d", &time)){ 
    return NULL;
  }

  TTime conv_time;
  pythonRealAsTTime(time, conv_time);

  error = User::SetHomeTime(conv_time);

  RETURN_ERROR_OR_PYNONE(error);
}


#ifdef USE_GLOBAL_DATA_HACK
extern "C" PyObject *
e32_globcnt(PyObject* /*self*/, PyObject* /*args*/)
{
  PyObject* rval = Py_BuildValue("i", *(PYTHON_GLOBALS->globptr));
  return rval;
}
#endif

/*
 *
 * Implementation of e32._uidcrc_app
 *
 */

class TMyCheckedAppUid : public TCheckedUid
{
public:
  TMyCheckedAppUid(TUint uid):
    TCheckedUid(TUidType(TUid::Uid(KDynamicLibraryUidValue),
                         TUid::Uid(KAppUidValue16),
                         TUid::Uid(uid))) {;}
  TUint CheckSum() {return Check();}
};

extern "C" PyObject *
e32_uidcrc_app(PyObject* /*self*/, PyObject *args)
{
  TUint uid;
  
  if (!PyArg_ParseTuple(args, "i", &uid))
    return NULL;
  
  TMyCheckedAppUid chk(uid);

  return Py_BuildValue("l", chk.CheckSum());
}

static int call_stdo(const char *buf, int n)
{
  PyObject* args = Py_BuildValue("(N)",
                                 PyUnicode_Decode(buf, n, NULL, NULL));
  PyObject* rval =
    PyEval_CallObject(SPyGetGlobalString("e32_stdo"), args);
  Py_XDECREF(args);
  Py_XDECREF(rval);
  return n;
}

static int print_stdo(const char *buf, int n)
{
  RFs rfs;
  
  if (rfs.Connect() == KErrNone) {
    RFile f;
    SPy_Python_globals* pg = PYTHON_GLOBALS;
    TPtrC fn((TUint16*)pg->stdo_buf, pg->stdo_buf_len);

    TInt error = f.Open(rfs, fn, EFileWrite);
    if (error == KErrNotFound)
      error = f.Create(rfs, fn, EFileWrite);
    
    TInt dummy;
    if ((error == KErrNone) && (f.Seek(ESeekEnd, dummy) == KErrNone))
      f.Write(TPtrC8((const TUint8*)buf, n));
    
    f.Close();
    rfs.Close();
  }

  return n;
}

extern "C" PyObject *
e32_stdo(PyObject* /*self*/, PyObject* args)
{
  PyObject* c;

  if (!PyArg_ParseTuple(args, "O", &c))
    return NULL;

  SPy_Python_globals* pg = PYTHON_GLOBALS;

  if (PyCallable_Check(c)) {
    if (SPyAddGlobalString("e32_stdo", c))
      return NULL;
    ((CSPyInterpreter*)pg->interpreter)->iStdO = &call_stdo;
  }
  else if (PyUnicode_Check(c)) {
    TPtr buf((TUint16*)pg->stdo_buf, 0x100);
    buf.Copy(PyUnicode_AsUnicode(c), PyUnicode_GetSize(c));
    pg->stdo_buf_len = PyUnicode_GetSize(c);
    ((CSPyInterpreter*)pg->interpreter)->iStdO = &print_stdo;
  }
  else if (c == Py_None) {
    ((CSPyInterpreter*)pg->interpreter)->iStdO = NULL;
  }
  else {
    PyErr_SetString(PyExc_TypeError, "callable or unicode expected");
    return NULL;
  }

  Py_INCREF(Py_None);
  return Py_None;
}

#ifndef EKA2
extern "C" PyObject *
e32_mem_info(PyObject* /*self*/)
{
  TInt error;
  TInt heap_sz, stack_sz, current_heap_usage,
    max_stack_usage, current_pyheap_usage;
  
  error = RThread().GetRamSizes(heap_sz, stack_sz);
  
  if (error == KErrNone) {
    (RThread().Heap())->AllocSize(current_heap_usage);
    ((CSPyInterpreter*)PYTHON_GLOBALS->interpreter)->
      iPyheap->AllocSize(current_pyheap_usage);
  }

  if (error == KErrNone) {
    TUint8* pbase = (RThread().Heap())->Base();
    TUint8* pstacklast = pbase - stack_sz;
    while (*pstacklast == 0x29)
      pstacklast++;
    max_stack_usage = pbase - pstacklast;
  }
  
  if (error != KErrNone)
    return SPyErr_SetFromSymbianOSErr(error);
  else
    return Py_BuildValue("(iiiii)", heap_sz, stack_sz,
                         current_heap_usage, max_stack_usage,
                         current_pyheap_usage);
}
#endif /*EKA2*/

extern "C" PyObject *
e32_strerror(PyObject* /*self*/, PyObject* args)
{
  int errcode;

  if (!PyArg_ParseTuple(args, "i", &errcode))
    return NULL;

  return SPyErr_SymbianOSErrAsString(errcode);
}

extern "C" {

  static const PyMethodDef e32_methods[] = {
    {"Ao_lock", (PyCFunction)new_e32_ao_object, METH_NOARGS, NULL},
    {"Ao_timer", (PyCFunction)new_e32_ao_timer_object, METH_NOARGS, NULL},
    {"ao_yield", (PyCFunction)e32_ao_yield, METH_NOARGS, NULL},
    {"ao_sleep", (PyCFunction)e32_ao_sleep, METH_VARARGS, NULL},
    {"ao_callgate", (PyCFunction)e32_ao_callgate, METH_VARARGS, NULL},
    {"_as_level", (PyCFunction)e32__as_level, METH_NOARGS, NULL},
    {"start_server", (PyCFunction)e32_start_server, METH_VARARGS, NULL},
    {"start_exe", (PyCFunction)e32_start_exe, METH_VARARGS, NULL},
    {"drive_list", (PyCFunction)e32_drive_list, METH_NOARGS, NULL},
    {"file_copy", (PyCFunction)e32_file_copy, METH_VARARGS, NULL},
    {"is_ui_thread", (PyCFunction)e32_is_ui_thread, METH_NOARGS, NULL},
    {"in_emulator", (PyCFunction)e32_in_emulator, METH_NOARGS, NULL},
    {"set_home_time", (PyCFunction)e32_set_home_time, METH_VARARGS, NULL},
    {"_uidcrc_app", (PyCFunction)e32_uidcrc_app, METH_VARARGS, NULL},
    {"_stdo", (PyCFunction)e32_stdo, METH_VARARGS, NULL},
#ifndef EKA2
    {"_mem_info", (PyCFunction)e32_mem_info, METH_NOARGS, NULL},
#endif /*EKA2*/
    {"strerror", (PyCFunction)e32_strerror, METH_VARARGS, NULL},
#ifdef USE_GLOBAL_DATA_HACK
    {"_globcnt", (PyCFunction)e32_globcnt, METH_NOARGS, NULL},
#endif
    {NULL,              NULL}           /* sentinel */
  };

  DL_EXPORT(void) inite32(void)
  {
    PyObject *m, *d;

    Ao_lock_type = c_Ao_lock_type;
    Ao_lock_type.ob_type = &PyType_Type;
    
    Ao_timer_type = c_Ao_timer_type;
    Ao_timer_type.ob_type = &PyType_Type;
    
    Ao_callgate_type = c_Ao_callgate_type;
    Ao_callgate_type.ob_type = &PyType_Type;

    m = Py_InitModule("e32", (PyMethodDef*)e32_methods);
    d = PyModule_GetDict(m);
    
    PyDict_SetItemString(d,"pys60_version_info", 
      Py_BuildValue("(iiisi)", PYS60_VERSION_MAJOR, PYS60_VERSION_MINOR, PYS60_VERSION_MICRO, 
                               PYS60_VERSION_TAG, PYS60_VERSION_SERIAL));
    PyDict_SetItemString(d,"pys60_version", Py_BuildValue("s", PYS60_VERSION_STRING));
    PyDict_SetItemString(d,"s60_version_info", Py_BuildValue("(ii)", 
#if SERIES60_VERSION==12
    1, 2
#endif
#if SERIES60_VERSION==20
    2, 0
#endif
#if SERIES60_VERSION==26
    2, 6
#endif
#if SERIES60_VERSION==28
    2, 8
#endif
#if SERIES60_VERSION==30
    3, 0
#endif
    ));    
  }
} /* extern "C" */