diff options
Diffstat (limited to 'src/fqterm/fqterm_python.cpp')
-rw-r--r-- | src/fqterm/fqterm_python.cpp | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/src/fqterm/fqterm_python.cpp b/src/fqterm/fqterm_python.cpp new file mode 100644 index 0000000..68b9385 --- /dev/null +++ b/src/fqterm/fqterm_python.cpp @@ -0,0 +1,826 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * + ***************************************************************************/ +
+ + +#include <QCustomEvent> +#include <QTextStream> +#include <QApplication> +#include <QDir> +#include <QFile> +#include <QByteArray> +#include <QMutex> +#include <QThread> +#include "fqterm.h" +#ifdef HAVE_PYTHON
+#include <Python.h>
+#endif +class SleeperThread : public QThread +{ +public: + static void msleep(unsigned long msecs) + { + QThread::msleep(msecs); + } +}; + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_PYTHON + +#include "fqterm_window.h" +#include "fqterm_buffer.h" +#include "fqterm_text_line.h" +#include "fqterm_param.h" +#include "fqterm_session.h" +#include "fqterm_python.h" +#include "fqterm_path.h" +#include "common.h" +namespace FQTerm { + +/* ************************************************************************** + * + * Pythons Embedding + * + * ***************************************************************************/ + +QString getException() { + PyObject *pType = NULL, *pValue = NULL, *pTb = NULL, *pName, *pTraceback; + + PyErr_Fetch(&pType, &pValue, &pTb); + + pName = PyString_FromString("traceback"); + pTraceback = PyImport_Import(pName); + Py_DECREF(pName); + + if (pTraceback == NULL) { + return "General Error in Python Callback"; + } + + pName = PyString_FromString("format_exception"); + PyObject *pRes = PyObject_CallMethodObjArgs(pTraceback, pName, pType, pValue, + pTb, NULL); + Py_DECREF(pName); + + Py_DECREF(pTraceback); + + Py_XDECREF(pType); + Py_XDECREF(pValue); + Py_XDECREF(pTb); + + if (pRes == NULL) { + return "General Error in Python Callback"; + } + + pName = PyString_FromString("string"); + PyObject *pString = PyImport_Import(pName); + Py_DECREF(pName); + + if (pString == NULL) { + return "General Error in Python Callback"; + } + + pName = PyString_FromString("join"); + PyObject *pErr = PyObject_CallMethodObjArgs(pString, pName, pRes, NULL); + Py_DECREF(pName); + + Py_DECREF(pString); + Py_DECREF(pRes); + + if (pErr == NULL) { + return "General Error in Python Callback"; + } + + QString str(PyString_AsString(pErr)); + Py_DECREF(pErr); + + return str; +} + +QString getErrOutputFile(FQTermWindow *lp) { + // file name + QString str2; + str2.setNum(long(lp)); + str2 += ".err"; + // path + return getPath(USER_CONFIG) + str2; +} + +// copy current artcle for back compatible use only +// for new coder please use getArticle +static PyObject *fqterm_copyArticle(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + FQTermWindow *termWindow_ = (FQTermWindow*)lp; + + QStringList strList; + QString strArticle; + QReadWriteLock& bufferLock = termWindow_->getSession()->getBufferLock(); + QReadLocker locker(&bufferLock); + while (1) { + // check it there is duplicated string + // it starts from the end in the range of one screen height + // so this is a non-greedy match + QString strTemp; + termWindow_->getSession()->getBuffer()->getTextLineInTerm(0)->getAllPlainText(strTemp); + strTemp = strTemp.trimmed(); + int i = 0; + int start = 0; + for (QStringList::Iterator it = strList.end(); + it != strList.begin() && i < termWindow_->getSession()->getBuffer()->getNumRows() - 1; // not exceeeding the last screen + --it, i++) { + if (*it != strTemp) { + continue; + } + QStringList::Iterator it2 = it; + bool dup = true; + // match more to see if its duplicated + for (int j = 0; j <= i; j++, it2++) { + QString str1; + termWindow_->getSession()->getBuffer()->getTextLineInTerm(j)->getAllPlainText(str1); + if (*it2 != str1.trimmed()) { + dup = false; + break; + } + } + if (dup) { + // set the start point + start = i + 1; + break; + } + } + // add new lines + for (i = start; i < termWindow_->getSession()->getBuffer()->getNumRows() - 1; i++) { + QString tmp; + termWindow_->getSession()->getBuffer()->getTextLineInTerm(i)->getAllPlainText(tmp); + strList += tmp.trimmed(); + } + + // the end of article + QString testEnd; + termWindow_->getSession()->getBuffer()->getTextLineInTerm(termWindow_->getSession()->getBuffer()->getNumRows() - 1)->getAllPlainText(testEnd); + if (testEnd.indexOf("%") == -1) { + break; + } + // continue + termWindow_->writeString_ts(" "); + + // TODO: fixme + if (!termWindow_->getSession()->getWaitCondition().wait(&bufferLock, 10000)) { + // // timeout + break; + } + } +#if defined(_OS_WIN32_) || defined(Q_OS_WIN32) + strArticle = strList.join("\r\n"); +#else + strArticle = strList.join("\n"); +#endif + + PyObject *py_text = PyString_FromString(strArticle.toUtf8()); + + Py_INCREF(py_text); + return py_text; +} + +static PyObject *fqterm_getArticle(PyObject *, PyObject *args) { + long lp; + int timeout; + int succeed = 1; + + if (!PyArg_ParseTuple(args, "li", &lp, &timeout)) { + return NULL; + } + FQTermWindow *termWindow_ = (FQTermWindow*)lp; + + QStringList strList; + QString strArticle; + QReadWriteLock& bufferLock = termWindow_->getSession()->getBufferLock(); + while (!bufferLock.tryLockForRead()) {} + while (1) { + // check it there is duplicated string + // it starts from the end in the range of one screen height + // so this is a non-greedy match + QString strTemp; + termWindow_->getSession()->getBuffer()->getTextLineInTerm(0)->getAllPlainText(strTemp); + strTemp = strTemp.trimmed(); + + int i = 0; + int start = 0; + for (QStringList::Iterator it = strList.end(); + it != strList.begin() && i < termWindow_->getSession()->getBuffer()->getNumRows() - 1; // not exceeeding the last screen + --it, i++) { + if (it == strList.end() || *it != strTemp) { + continue; + } + QStringList::Iterator it2 = it; + bool dup = true; + // match more to see if its duplicated + for (int j = 0; j <= i && it2 != strList.end(); j++, it2++) { + QString str1; + termWindow_->getSession()->getBuffer()->getTextLineInTerm(j)->getAllPlainText(str1); + if (*it2 != str1.trimmed()) { + dup = false; + break; + } + } + if (dup) { + // set the start point + start = i + 1; + break; + } + } + // add new lines + for (i = start; i < termWindow_->getSession()->getBuffer()->getNumRows() - 1; i++) { + QString tmp; + termWindow_->getSession()->getBuffer()->getTextLineInTerm(i)->getAllPlainText(tmp); + strList += tmp.trimmed(); + } + + // the end of article + QString testEnd; + termWindow_->getSession()->getBuffer()->getTextLineInTerm(termWindow_->getSession()->getBuffer()->getNumRows() - 1)->getAllPlainText(testEnd); + if (testEnd.indexOf("%") == -1) { + break; + } + // continue + termWindow_->writeString_ts(" "); + + // TODO: fixme + if (!termWindow_->getSession()->getWaitCondition().wait(&bufferLock, timeout)) { // timeout + succeed = 0; + break; + } + } + bufferLock.unlock(); + if (succeed) + strArticle = strList.join(OS_NEW_LINE); + + PyObject *py_res = Py_BuildValue("si", (const char*)strArticle.toUtf8().data(), + succeed); + + Py_INCREF(py_res); + + return py_res; + +} + +static PyObject *fqterm_formatError(PyObject *, PyObject *args) { + long lp; + + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + QString strErr; + QString filename = getErrOutputFile((FQTermWindow*)lp); + + QDir d; + if (d.exists(filename)) { + QFile file(filename); + file.open(QIODevice::ReadOnly); + QTextStream is(&file); + while (!is.atEnd()) { + strErr += is.readLine(); // line of text excluding '\n' + strErr += '\n'; + } + file.close(); + d.remove(filename); + } + + if (!strErr.isEmpty()) { + ((FQTermWindow*)lp)->getPythonErrorMessage() = strErr; + // TODO: fixme + //qApp->postEvent((FQTermWindow*)lp, new QCustomEvent(PYE_ERROR)); + } else { + // TODO: fixme + //qApp->postEvent((FQTermWindow*)lp, new QCustomEvent(PYE_FINISH)); + } + + + Py_INCREF(Py_None); + return Py_None; +} + +// caret x +static PyObject *fqterm_caretX(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + int x = ((FQTermWindow*)lp)->getSession()->getBuffer()->getCaretColumn(); + PyObject *py_x = Py_BuildValue("i", x); + Py_INCREF(py_x); + return py_x; +} + +// caret y +static PyObject *fqterm_caretY(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + int y = ((FQTermWindow*)lp)->getSession()->getBuffer()->getCaretRow(); + PyObject *py_y = Py_BuildValue("i", y); + Py_INCREF(py_y); + return py_y; + +} + +// columns +static PyObject *fqterm_columns(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + int numColumns = ((FQTermWindow*)lp)->getSession()->getBuffer()->getNumColumns(); + PyObject *py_columns = Py_BuildValue("i", numColumns); + + Py_INCREF(py_columns); + return py_columns; + +} + +// rows +static PyObject *fqterm_rows(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + int rows = ((FQTermWindow*)lp)->getSession()->getBuffer()->getNumRows(); + PyObject *py_rows = Py_BuildValue("i", rows); + + Py_INCREF(py_rows); + return py_rows; +} + +// sned string to server +static PyObject *fqterm_sendString(PyObject *, PyObject *args) { + char *pstr; + long lp; + + if (!PyArg_ParseTuple(args, "ls", &lp, &pstr)) { + return NULL; + } + + ((FQTermWindow*)lp)->writeRawString_ts(U82U(pstr)); + + Py_INCREF(Py_None); + return Py_None; +} + +// input should be utf8. +// same as above except parsing string first "\n" "^p" etc +static PyObject *fqterm_sendParsedString(PyObject *, PyObject *args) { + char *pstr; + long lp; + int len; + + if (!PyArg_ParseTuple(args, "ls", &lp, &pstr)) { + return NULL; + } + len = strlen(pstr); + + ((FQTermWindow*)lp)->writeString_ts(U82U(pstr)); + + Py_INCREF(Py_None); + return Py_None; +} + +// get text at line +static PyObject *fqterm_getText(PyObject *, PyObject *args) { + long lp; + int numRows; + if (!PyArg_ParseTuple(args, "li", &lp, &numRows)) { + return NULL; + } + QString str; + if (numRows < ((FQTermWindow*)lp)->getSession()->getBuffer()->getNumRows()) + ((FQTermWindow*)lp)->getSession()->getBuffer()->getTextLineInTerm(numRows)->getAllPlainText(str); + + PyObject *py_text = PyString_FromString(U2U8(str)); + + Py_INCREF(py_text); + return py_text; +} + +// get text with attributes +static PyObject *fqterm_getAttrText(PyObject *, PyObject *args) { + long lp; + int numRows; + if (!PyArg_ParseTuple(args, "li", &lp, &numRows)) { + return NULL; + } + + QString str; + if (numRows < ((FQTermWindow*)lp)->getSession()->getBuffer()->getNumRows()) + ((FQTermWindow*)lp)->getSession()->getBuffer()->getTextLineInTerm(numRows)->getAllAnsiText(str); + + PyObject *py_text = PyString_FromString(U2U8(str)); + + Py_INCREF(py_text); + return py_text; +} + +// is host connected +static PyObject *fqterm_isConnected(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + bool connected = ((FQTermWindow*)lp)->isConnected(); + PyObject *py_connected = Py_BuildValue("i", connected ? 1 : 0); + + Py_INCREF(py_connected); + return py_connected; +} + +// disconnect from host +static PyObject *fqterm_disconnect(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + ((FQTermWindow*)lp)->disconnect(); + + Py_INCREF(Py_None); + return Py_None; +} + +// reconnect to host +static PyObject *fqterm_reconnect(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + ((FQTermWindow*)lp)->getSession()->reconnect(); + + Py_INCREF(Py_None); + return Py_None; +} + +// bbs encoding 0-GBK 1-BIG5 +//FIXME: UTF8 and HKSCS +static PyObject *fqterm_getBBSCodec(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + PyObject *py_codec = PyString_FromString(((FQTermWindow*)lp) + ->getSession()->param().serverEncodingID_ == 0 ? "GBK" : "Big5"); + Py_INCREF(py_codec); + + return py_codec; +} + +// host address +static PyObject *fqterm_getAddress(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + PyObject *py_addr = PyString_FromString(((FQTermWindow*)lp) + ->getSession()->param().hostAddress_.toLocal8Bit()); + Py_INCREF(py_addr); + return py_addr; +} + +// host port number +static PyObject *fqterm_getPort(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + PyObject *py_port = Py_BuildValue("i", ((FQTermWindow*)lp)->getSession()->param().port_); + Py_INCREF(py_port); + return py_port; +} + +// connection protocol 0-telnet 1-SSH1 2-SSH2 +static PyObject *fqterm_getProtocol(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + PyObject *py_port = Py_BuildValue("i", ((FQTermWindow*)lp) + ->getSession()->param().protocolType_); + Py_INCREF(py_port); + return py_port; +} + +// key to reply msg +static PyObject *fqterm_getReplyKey(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + PyObject *py_key = PyString_FromString(((FQTermWindow*)lp) + ->getSession()->param().replyKeyCombination_.toLocal8Bit()); + Py_INCREF(py_key); + return py_key; +} + +// url under mouse +static PyObject *fqterm_getURL(PyObject *, PyObject *args) { + long lp; + if (!PyArg_ParseTuple(args, "l", &lp)) { + return NULL; + } + + PyObject *py_url = PyString_FromString(((FQTermWindow*)lp)->getSession()->getUrl().toUtf8().constData()); + Py_INCREF(py_url); + return py_url; +} + +// preview image link +static PyObject *fqterm_previewImage(PyObject *, PyObject *args) { + long lp; + char *url; + if (!PyArg_ParseTuple(args, "ls", &lp, &url)) { + return NULL; + } + + ((FQTermWindow*)lp)->getHttpHelper(url, true); + + Py_INCREF(Py_None); + return Py_None; + +} + +// convert string from UTF8 to specified encoding +static PyObject *fqterm_fromUTF8(PyObject *, PyObject *args) { + char *str, *enc; + if (!PyArg_ParseTuple(args, "ss", &str, &enc)) { + return NULL; + } + QTextCodec *encodec = QTextCodec::codecForName(enc); + QTextCodec *utf8 = QTextCodec::codecForName("utf8"); + + PyObject *py_str = PyString_FromString(encodec->fromUnicode(utf8->toUnicode + (str))); + Py_INCREF(py_str); + return py_str; +} + +// convert string from specified encoding to UTF8 +static PyObject *fqterm_toUTF8(PyObject *, PyObject *args) { + char *str, *enc; + if (!PyArg_ParseTuple(args, "ss", &str, &enc)) { + return NULL; + } + QTextCodec *encodec = QTextCodec::codecForName(enc); + QTextCodec *utf8 = QTextCodec::codecForName("utf8"); + + PyObject *py_str = PyString_FromString(utf8->fromUnicode(encodec->toUnicode + (str))); + Py_INCREF(py_str); + return py_str; +} + +static PyObject *fqterm_wait(PyObject *, PyObject *args) { + long t; + if (!PyArg_ParseTuple(args, "l", &t)) { + return NULL; + } + SleeperThread::msleep(t); + + Py_INCREF(Py_None); + return Py_None; +} + +PyMethodDef fqterm_methods[] = { + { + "formatError", (PyCFunction)fqterm_formatError, METH_VARARGS, + "get the traceback info" + } + + , + + { + "getArticle", (PyCFunction)fqterm_getArticle, METH_VARARGS, + "copy current article" + } + + , + + { + "copyArticle", (PyCFunction)fqterm_copyArticle, METH_VARARGS, + "copy current article (obsolete)" + } + + , + + { + "getText", (PyCFunction)fqterm_getText, METH_VARARGS, "get text at line#" + } + + , + + { + "getAttrText", (PyCFunction)fqterm_getAttrText, METH_VARARGS, + "get attr text at line#" + } + + , + + { + "sendString", (PyCFunction)fqterm_sendString, METH_VARARGS, + "send string to server" + } + + , + + { + "sendParsedString", (PyCFunction)fqterm_sendParsedString, METH_VARARGS, + "send string with escape" + } + + , + + { + "caretX", (PyCFunction)fqterm_caretX, METH_VARARGS, "caret x" + } + + , + + { + "caretY", (PyCFunction)fqterm_caretY, METH_VARARGS, "caret y" + } + + , + + { + "columns", (PyCFunction)fqterm_columns, METH_VARARGS, "screen width" + } + + , + + { + "rows", (PyCFunction)fqterm_rows, METH_VARARGS, "screen height" + } + + , + + { + "isConnected", (PyCFunction)fqterm_isConnected, METH_VARARGS, + "connected to server or not" + } + + , + + { + "disconnect", (PyCFunction)fqterm_disconnect, METH_VARARGS, + "disconnect from server" + } + + , + + { + "reconnect", (PyCFunction)fqterm_reconnect, METH_VARARGS, "reconnect" + } + + , + + { + "getBBSCodec", (PyCFunction)fqterm_getBBSCodec, METH_VARARGS, + "get the bbs encoding, GBK or Big5" + } + + , + + { + "getAddress", (PyCFunction)fqterm_getAddress, METH_VARARGS, + "get the bbs address" + } + + , + + { + "getPort", (PyCFunction)fqterm_getPort, METH_VARARGS, "get the bbs port number" + } + + , + + { + "getProtocol", (PyCFunction)fqterm_getProtocol, METH_VARARGS, + "get the bbs protocol, 0/1/2 TELNET/SSH1/SSH2" + } + + , + + { + "getReplyKey", (PyCFunction)fqterm_getReplyKey, METH_VARARGS, + "get the key to reply messages" + } + + , + + { + "getURL", (PyCFunction)fqterm_getURL, METH_VARARGS, + "get the url string under mouse" + } + + , + + { + "previewImage", (PyCFunction)fqterm_previewImage, METH_VARARGS, + "preview the image link" + } + + , + + { + "fromUTF8", (PyCFunction)fqterm_fromUTF8, METH_VARARGS, + "decode from utf8 to string in specified codec" + } + + , + + { + "toUTF8", (PyCFunction)fqterm_toUTF8, METH_VARARGS, + "decode from string in specified codec to utf8" + } + + , + + { + "wait", (PyCFunction)fqterm_wait, METH_VARARGS, + "wait for x ms" + } + + , + + { + NULL, (PyCFunction)NULL, 0, NULL + } +}; + + + +FQTermPythonHelper::FQTermPythonHelper() + : mainThreadState_(NULL) { + + // initialize Python + Py_Initialize(); + // initialize thread support + PyEval_InitThreads(); + // save a pointer to the main PyThreadState object + mainThreadState_ = PyThreadState_Get(); + // add path + PyRun_SimpleString("import sys\n"); + QString pathCmd; + pathCmd = "sys.path.insert(0,'"; + pathCmd += getPath(RESOURCE)+"script')"; + PyRun_SimpleString(fq_strdup(pathCmd.toUtf8().data())); + + Py_InitModule4("fqterm", fqterm_methods, + NULL,(PyObject*)NULL,PYTHON_API_VERSION); + // release the lock + PyEval_ReleaseLock(); +} + +FQTermPythonHelper::~FQTermPythonHelper() { + // shut down the interpreter + PyInterpreterState * mainInterpreterState = mainThreadState_->interp; + // create a thread state object for this thread + PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState); + PyThreadState_Swap(myThreadState); + PyEval_AcquireLock(); + Py_Finalize(); +} + +} // namespace FQTerm + +#endif //HAVE_PYTHON
\ No newline at end of file |