This commit is contained in:
concordia
2024-04-28 03:24:23 -05:00
parent 926a1b9f4a
commit 224327480a
35 changed files with 2117 additions and 1 deletions

View File

@@ -79,6 +79,9 @@ qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs)
qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs) qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs)
qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs) qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs)
# Clearpilot tools
qt_env.Program("/data/openpilot/system/clearpilot/tools/qt_shell", ["/data/openpilot/system/clearpilot/tools/qt_shell.cc"], LIBS=qt_libs)
# build main UI # build main UI
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs) qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs)
if GetOption('extras'): if GetOption('extras'):

BIN
system/clearpilot/tools/qt_shell Executable file

Binary file not shown.

View File

@@ -0,0 +1,76 @@
#include <QApplication>
#include <QWidget>
#include <QTextEdit>
#include <QProcess>
#include <QVBoxLayout>
#include <QFont>
#include <QScreen>
#include <QScrollBar>
#include <QGuiApplication>
#include <qpa/qplatformnativeinterface.h>
#include <wayland-client-protocol.h>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
if (argc < 2) {
printf("Usage: %s '<command>'\n", argv[0]);
return 1;
}
QWidget window;
window.setWindowTitle("Shell Command Output Viewer");
window.setStyleSheet("background-color: black;");
window.showFullScreen();
auto windowHandle = window.windowHandle();
if (!windowHandle) {
fprintf(stderr, "Error: Unable to obtain window handle.\n");
return 1;
}
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
auto *s = static_cast<wl_surface*>(native->nativeResourceForWindow("surface", windowHandle));
if (!s) {
fprintf(stderr, "Error: Unable to obtain native Wayland surface.\n");
return 1;
}
wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270);
wl_surface_commit(s);
window.setFixedSize(2160, 1080);
QVBoxLayout *layout = new QVBoxLayout(&window);
QTextEdit *outputDisplay = new QTextEdit;
outputDisplay->setFont(QFont("Consolas", 32));
outputDisplay->setReadOnly(true);
outputDisplay->setFixedSize(2160, 1080);
outputDisplay->setStyleSheet("color: white; background-color: black;");
outputDisplay->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Hide the vertical scrollbar
layout->addWidget(outputDisplay);
QProcess process;
QObject::connect(&process, &QProcess::readyReadStandardOutput, [&]() {
static QStringList lines;
QString output = process.readAllStandardOutput();
lines += output.split("\n", QString::SkipEmptyParts);
while (lines.size() > 100) {
lines.removeFirst();
}
outputDisplay->setPlainText(lines.join("\n"));
outputDisplay->verticalScrollBar()->setValue(outputDisplay->verticalScrollBar()->maximum());
});
QObject::connect(&process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), [&]() {
app.quit();
});
QString command = argv[1];
process.start(QString("bash -c \"%1\"").arg(command));
return app.exec();
}

View File

@@ -1 +1 @@
sudo su comma -c "python3 shell.py \"echo hello; sleep 5\" sudo su comma -c "python3 /data/openpilot/system/clearpilot/tools/shell.py \"echo hello; sleep 5\"

View File

@@ -0,0 +1,785 @@
/**
Change log:
(C) 2005 by Houssem BDIOUI <houssem.bdioui@gmail.com>
(C) 2010 by YoungTaek Oh. (migrated to Qt4)
(C) 2014-2015 Igor Malinovskiy
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.
*/
// #include "qconsole.h"
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QVBoxLayout>
#include <QApplication>
#include <QScrollBar>
#include <QDesktopWidget>
//#define USE_POPUP_COMPLETER
#define WRITE_ONLY QIODevice::WriteOnly
QSize PopupListWidget::sizeHint() const
{
QAbstractItemModel *model = this->model();
QAbstractItemDelegate *delegate = this->itemDelegate();
const QStyleOptionViewItem sovi;
int left, top, right, bottom = 0;
QMargins margin = this->contentsMargins();
top = margin.top();
bottom = margin.bottom();
left = margin.left();
right = margin.right();
const int vOffset = top + bottom;
const int hOffset = left + right;
bool vScrollOn = false;
int height = 0;
int width = 0;
for (int i=0; i<this->count(); ++i) {
QModelIndex index = model->index(i, 0);
QSize itemSizeHint = delegate->sizeHint(sovi, index);
if (itemSizeHint.width() > width)
width = itemSizeHint.width();
// height
const int nextHeight = height + itemSizeHint.height();
if (nextHeight + vOffset < this->maximumHeight())
height = nextHeight;
else {
// early termination
vScrollOn = true;
break;
}
}
QSize sizeHint(width + hOffset, 0);
sizeHint.setHeight(height + vOffset);
if (vScrollOn) {
int scrollWidth = this->verticalScrollBar()->sizeHint().width();
sizeHint.setWidth(sizeHint.width() + scrollWidth);
}
return sizeHint;
}
void PopupListWidget::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Tab ||
e->key() == Qt::Key_Return)
Q_EMIT itemActivated(currentItem());
else
QListWidget::keyPressEvent(e);
}
PopupCompleter::PopupCompleter(const QStringList& sl, QWidget *parent)
: QDialog(parent, Qt::Popup)
{
setModal(true);
listWidget_ = new PopupListWidget();
listWidget_->setMaximumHeight(200);
qDebug() << "sizeHint(): " << listWidget_->sizeHint();
Q_FOREACH(QString str, sl) {
QListWidgetItem *item = new QListWidgetItem;
item->setText(str);
listWidget_->addItem(item);
}
qDebug() << "sizeHint(): " << listWidget_->sizeHint();
listWidget_->setFixedSize(listWidget_->sizeHint());
QLayout *layout = new QVBoxLayout();
layout->setSizeConstraint(QLayout::SetFixedSize);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(listWidget_);
setLayout(layout);
// connect signal
connect(listWidget_, SIGNAL(itemActivated(QListWidgetItem *)),
SLOT(onItemActivated(QListWidgetItem*)));
}
PopupCompleter::~PopupCompleter()
{
}
void PopupCompleter::showEvent(QShowEvent *event)
{
listWidget_->setFocus();
}
void PopupCompleter::onItemActivated(QListWidgetItem *event)
{
selected_ = event->text();
done(QDialog::Accepted);
}
/**
* @brief execute PopupCompleter at appropriate position.
*
* @param parent Parent of this popup completer. usually QConsole.
* @return see QDialog::exec
* @see QDialog::exec
*/
int PopupCompleter::exec(QTextEdit *parent)
{
QSize popupSizeHint = this->sizeHint();
QRect cursorRect = parent->cursorRect();
QPoint globalPt = parent->mapToGlobal(cursorRect.bottomRight());
QDesktopWidget *dsk = QApplication::desktop();
QRect screenGeom = dsk->screenGeometry(dsk->screenNumber(this));
if (globalPt.y() + popupSizeHint.height() > screenGeom.height()) {
globalPt = parent->mapToGlobal(cursorRect.topRight());
globalPt.setY(globalPt.y() - popupSizeHint.height());
}
this->move(globalPt);
this->setFocus();
return QDialog::exec();
}
/**
* returns a common word of the given list
*
* @param list String list
*
* @return common word in the given string.
*/
static
QString getCommonWord(QStringList& list)
{
QChar ch;
QVector<QString> strarray = list.toVector();
QString common;
int col = 0, min_len;
bool cont = true;
// get minimum length
min_len = strarray.at(0).size();
for (int i=1; i<strarray.size(); ++i) {
const int len = strarray.at(i).size();
if (len < min_len)
min_len = len;
}
while(col < min_len) {
ch = strarray.at(0)[col];
for (int i=1; i<strarray.size(); ++i) {
const QString& current_string = strarray.at(i);
if (ch != current_string[col])
{
cont = false;
break;
}
}
if (!cont)
break;
common.push_back(ch);
++col;
}
return common;
}
////////////////////////////////////////////////////////////////////////////////
//Clear the console
void QConsole::clear()
{
QTextEdit::clear();
}
//Reset the console
void QConsole::reset(const QString &welcomeText)
{
clear();
append(welcomeText);
append("");
//init attributes
historyIndex = 0;
history.clear();
recordedScript.clear();
}
//QConsole constructor (init the QTextEdit & the attributes)
QConsole::QConsole(QWidget *parent, const QString &welcomeText)
: QTextEdit(parent), errColor_(Qt::red),
outColor_(Qt::white), completionColor(Qt::darkGreen),
promptLength(0), promptParagraph(0), isLocked(false)
{
// Disable accepting rich text from user
setAcceptRichText(false);
//Disable undo/redo
setUndoRedoEnabled(false);
//Disable context menu
//This menu is useful for undo/redo, cut/copy/paste, del, select all,
// see function QConsole::contextMenuEvent(...)
//setContextMenuPolicy(Qt::NoContextMenu);
//resets the console
reset(welcomeText);
const int tabwidth = QFontMetrics(currentFont()).width('a') * 4;
setTabStopWidth(tabwidth);
}
//Sets the prompt and cache the prompt length to optimize the processing speed
void QConsole::setPrompt(const QString &newPrompt, bool display)
{
prompt = newPrompt;
promptLength = prompt.length();
//display the new prompt
if (display)
displayPrompt();
}
//Displays the prompt and move the cursor to the end of the line.
void QConsole::displayPrompt()
{
//Prevent previous text displayed to be undone
setUndoRedoEnabled(false);
//displays the prompt
setTextColor(cmdColor_);
QTextCursor cur = textCursor();
cur.insertText(prompt);
cur.movePosition(QTextCursor::EndOfLine);
setTextCursor(cur);
//Saves the paragraph number of the prompt
promptParagraph = cur.blockNumber();
//Enable undo/redo for the actual command
setUndoRedoEnabled(true);
}
void QConsole::setFont(const QFont& f) {
QTextCharFormat format;
QTextCursor oldCursor = textCursor();
format.setFont(f);
selectAll();
textCursor().setBlockCharFormat(format);
setCurrentFont(f);
setTextCursor(oldCursor);
}
//Give suggestions to autocomplete a command (should be reimplemented)
//the return value of the function is the string list of all suggestions
QStringList QConsole::suggestCommand(const QString&, QString& prefix)
{
prefix = "";
return QStringList();
}
//Treat the tab key & autocomplete the current command
void QConsole::handleTabKeyPress()
{
QString command = getCurrentCommand();
QString commandPrefix;
QStringList sl = suggestCommand(command, commandPrefix);
if (sl.count() == 0)
textCursor().insertText("\t");
else {
if (sl.count() == 1)
replaceCurrentCommand(commandPrefix + sl[0]);
else
{
// common word completion
QString commonWord = getCommonWord(sl);
command = commonWord;
PopupCompleter *popup = new PopupCompleter(sl);
if (popup->exec(this) == QDialog::Accepted)
replaceCurrentCommand(commandPrefix + popup->selected());
delete popup;
}
}
}
// If return pressed, do the evaluation and append the result
void QConsole::handleReturnKeyPress()
{
//Get the command to validate
QString command = getCurrentCommand();
//execute the command and get back its text result and its return value
if (isCommandComplete(command))
pExecCommand(command);
else
{
append("");
moveCursor(QTextCursor::EndOfLine);
}
}
bool QConsole::handleBackspaceKeyPress()
{
QTextCursor cur = textCursor();
const int col = cur.columnNumber();
const int blk = cur.blockNumber();
if (blk == promptParagraph && col == promptLength)
return true;
return false;
}
void QConsole::handleUpKeyPress()
{
if (history.count())
{
QString command = getCurrentCommand();
do
{
if (historyIndex)
historyIndex--;
else
{
break;
}
} while(history[historyIndex] == command);
replaceCurrentCommand(history[historyIndex]);
}
}
void QConsole::handleDownKeyPress()
{
if (history.count())
{
QString command = getCurrentCommand();
do
{
if (++historyIndex >= history.size())
{
historyIndex = history.size() - 1;
break;
}
} while(history[historyIndex] == command);
replaceCurrentCommand(history[historyIndex]);
}
}
void QConsole::setHome(bool select)
{
QTextCursor cursor = textCursor();
cursor.movePosition(QTextCursor::StartOfBlock, select ? QTextCursor::KeepAnchor :
QTextCursor::MoveAnchor);
if(textCursor().blockNumber() == promptParagraph)
{
cursor.movePosition(QTextCursor::Right, select ? QTextCursor::KeepAnchor :
QTextCursor::MoveAnchor,
promptLength);
}
setTextCursor(cursor);
}
//Reimplemented key press event
void QConsole::keyPressEvent( QKeyEvent *e )
{
if (isLocked)
return;
//If the user wants to copy or cut outside
//the editing area we perform a copy
if(textCursor().hasSelection())
{
if(e->modifiers() == Qt::CTRL)
{
if( e->matches(QKeySequence::Cut) )
{
e->accept();
if(!isInEditionZone())
{
copy();
}
else
{
cut();
}
return;
}
else if(e->matches(QKeySequence::Copy) )
{
e->accept();
copy();
}
else
{
QTextEdit::keyPressEvent( e );
return;
}
}
}
/*
// if the cursor out of editing zone put it back first
if(!isInEditionZone())
{
QTextCursor editCursor = textCursor();
editCursor.setPosition(oldEditPosition);
setTextCursor(editCursor);
}
*/
// control is pressed
if ( (e->modifiers() & Qt::ControlModifier) && (e->key() == Qt::Key_C) )
{
if ( isSelectionInEditionZone())
{
//If Ctrl + C pressed, then undo the current commant
//append("");
//displayPrompt();
//(Thierry Belair:)I humbly suggest that ctrl+C copies the text, as is expected,
//and indicated in the contextual menu
copy();
return;
}
}
else {
switch (e->key()) {
case Qt::Key_Tab:
if(isSelectionInEditionZone())
{
handleTabKeyPress();
}
return;
case Qt::Key_Enter:
case Qt::Key_Return:
if (isSelectionInEditionZone())
{
handleReturnKeyPress();
}
// ignore return key
return;
case Qt::Key_Backspace:
if (handleBackspaceKeyPress() || !isSelectionInEditionZone())
return;
break;
case Qt::Key_Home:
setHome(e->modifiers() & Qt::ShiftModifier);
case Qt::Key_Down:
if (isInEditionZone())
{
handleDownKeyPress();
}
return;
case Qt::Key_Up:
if (isInEditionZone())
{
handleUpKeyPress();
}
return;
//Default behaviour
case Qt::Key_End:
case Qt::Key_Left:
case Qt::Key_Right:
break;
default:
if (textCursor().hasSelection() ) {
if (!isSelectionInEditionZone())
{
moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
}
break;
}
else
{ //no selection
//when typing normal characters,
//make sure the cursor is positionned in the
//edition zone
if ( !isInEditionZone() )
{
moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
}
}
} //end of switch
} //end of else : no control pressed
QTextEdit::keyPressEvent( e );
}
//Get the current command
QString QConsole::getCurrentCommand()
{
QTextCursor cursor = textCursor(); //Get the current command: we just remove the prompt
cursor.movePosition(QTextCursor::StartOfBlock);
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, promptLength);
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
QString command = cursor.selectedText();
cursor.clearSelection();
return command;
}
//Replace current command with a new one
void QConsole::replaceCurrentCommand(const QString &newCommand)
{
QTextCursor cursor = textCursor();
cursor.movePosition(QTextCursor::StartOfLine);
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, promptLength);
cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
cursor.insertText(newCommand);
}
//default implementation: command always complete
bool QConsole::isCommandComplete(const QString &)
{
return true;
}
//Tests whether the cursor is in th edition zone or not (after the prompt
//or in the next lines (in case of multi-line mode)
bool QConsole::isInEditionZone()
{
const int para = textCursor().blockNumber();
const int index = textCursor().columnNumber();
return (para > promptParagraph) || ( (para == promptParagraph) && (index >= promptLength) );
}
//Tests whether position (in parameter) is in the edition zone or not (after the prompt
//or in the next lines (in case of multi-line mode)
bool QConsole::isInEditionZone(const int& pos)
{
QTextCursor cur = textCursor();
cur.setPosition(pos);
const int para = cur.blockNumber();
const int index = cur.columnNumber();
return (para > promptParagraph) || ( (para == promptParagraph) && (index >= promptLength) );
}
//Tests whether the current selection is in th edition zone or not
bool QConsole::isSelectionInEditionZone()
{
QTextCursor cursor(document());
int range[2];
range[0] = textCursor().selectionStart();
range[1] = textCursor().selectionEnd();
for (int i = 0; i < 2; i++)
{
cursor.setPosition(range[i]);
int para = cursor.blockNumber();
int index = cursor.columnNumber();
if ((para <= promptParagraph) && ( (para != promptParagraph) || (index < promptLength) ))
{
return false;
}
}
return true;
}
//Basically, puts the command into the history list
//And emits a signal (should be called by reimplementations)
QString QConsole::addCommandToHistory(const QString &command)
{
//Add the command to the recordedScript list
recordedScript.append(command);
//update the history and its index
QString modifiedCommand = command;
modifiedCommand.replace("\n", "\\n");
history.append(modifiedCommand);
historyIndex = history.size();
//emit the commandExecuted signal
Q_EMIT commandAddedToHistory(modifiedCommand);
return "";
}
//pExecCommand(QString) executes the command and displays back its result
void QConsole::pExecCommand(const QString &command)
{
isLocked = true;
addCommandToHistory(command);
emit execCommand(command);
}
void QConsole::printCommandExecutionResults(const QString &result, ResultType type)
{
//According to the return value, display the result either in red or in blue
if (type == ResultType::Error)
setTextColor(errColor_);
else
setTextColor(outColor_);
append(result);
//Display the prompt again
if (type == ResultType::Complete || type == ResultType::Error) {
if (!result.endsWith("\n"))
append("\n");
isLocked = false;
displayPrompt();
}
moveCursor(QTextCursor::End);
}
//Change paste behaviour
void QConsole::insertFromMimeData(const QMimeData *source)
{
if (isSelectionInEditionZone())
{
QTextEdit::insertFromMimeData(source);
}
}
//Implement paste with middle mouse button
void QConsole::mousePressEvent(QMouseEvent* event)
{
oldPosition = textCursor().position();
if (event->button() == Qt::MidButton)
{
copy();
QTextCursor cursor = cursorForPosition(event->pos());
setTextCursor(cursor);
paste();
return;
}
QTextEdit::mousePressEvent(event);
}
//Redefinition of the dropEvent to have a copy paste
//instead of a cut paste when copying out of the
//editable zone
void QConsole::dropEvent ( QDropEvent * event)
{
if(!isInEditionZone())
{
//Execute un drop a drop at the old position
//if the drag started out of the editable zone
QTextCursor cur = textCursor();
cur.setPosition(oldPosition);
setTextCursor(cur);
}
//Execute a normal drop
QTextEdit::dropEvent(event);
}
void QConsole::dragMoveEvent( QDragMoveEvent * event)
{
//Get a cursor for the actual mouse position
QTextCursor cur = textCursor();
cur.setPosition(cursorForPosition(event->pos()).position());
if(!isInEditionZone(cursorForPosition(event->pos()).position()))
{
//Ignore the event if out of the editable zone
event->ignore(cursorRect(cur));
}
else
{
//Accept the event if out of the editable zone
event->accept(cursorRect(cur));
}
}
void QConsole::contextMenuEvent ( QContextMenuEvent * event)
{
if (isLocked)
return;
QMenu *menu = new QMenu(this);
QAction *undo = new QAction(tr("Undo"), this);
undo->setShortcut(tr("Ctrl+Z"));
QAction *redo = new QAction(tr("Redo"), this);
redo->setShortcut(tr("Ctrl+Y"));
QAction *cut = new QAction(tr("Cut"), this);
cut->setShortcut(tr("Ctrl+X"));
QAction *copy = new QAction(tr("Copy"), this);
copy->setShortcut(tr("Ctrl+Ins"));
QAction *paste = new QAction(tr("Paste"), this);
paste->setShortcut(tr("Ctrl+V"));
QAction *del = new QAction(tr("Delete"), this);
del->setShortcut(tr("Del"));
QAction *selectAll = new QAction(tr("Select All"), this);
selectAll->setShortcut(tr("Ctrl+A"));
menu->addAction(undo);
menu->addAction(redo);
menu->addSeparator();
menu->addAction(cut);
menu->addAction(copy);
menu->addAction(paste);
menu->addAction(del);
menu->addSeparator();
menu->addAction(selectAll);
connect(undo, SIGNAL(triggered()), this, SLOT(undo()));
connect(redo, SIGNAL(triggered()), this, SLOT(redo()));
connect(cut, SIGNAL(triggered()), this, SLOT(cut()));
connect(copy, SIGNAL(triggered()), this, SLOT(copy()));
connect(paste, SIGNAL(triggered()), this, SLOT(paste()));
connect(del, SIGNAL(triggered()), this, SLOT(del()));
connect(selectAll, SIGNAL(triggered()), this, SLOT(selectAll()));
menu->exec(event->globalPos());
delete menu;
}
void QConsole::cut()
{
//Cut only in the editing zone,
//perfom a copy otherwise
if(isSelectionInEditionZone())
{
QTextEdit::cut();
return;
}
QTextEdit::copy();
}
/*
//Allows pasting with middle mouse button (x window)
//when clicking outside of the edition zone
void QConsole::paste()
{
restoreOldPosition();
QTextEdit::paste();
}
*/
void QConsole::del()
{
//Delete only in the editing zone
if(isInEditionZone())
{
textCursor().movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
textCursor().deleteChar();
}
}
void QConsole::correctPathName(QString& pathName)
{
if(pathName.contains(tr(":\\")))
{
pathName.replace('\\', tr("/"));
}
}

View File

@@ -0,0 +1,211 @@
/**
Change log:
(C) 2005 by Houssem BDIOUI <houssem.bdioui@gmail.com>
(C) 2010 by YoungTaek Oh. (migrated to Qt4)
(C) 2014-2015 Igor Malinovskiy
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.
*/
#pragma once
#include <QStringList>
#include <QTextEdit>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QMenu>
#include <QDialog>
#include <QListWidget>
#include <QDebug>
/**
* Subclasssing QListWidget
*
* @author YoungTaek Oh
*/
class PopupListWidget : public QListWidget
{
Q_OBJECT
public:
PopupListWidget(QWidget *parent = 0): QListWidget(parent) {
setUniformItemSizes(true);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
virtual ~PopupListWidget() { }
protected:
virtual QSize sizeHint() const;
virtual void keyPressEvent(QKeyEvent *e);
};
/**
* Popup Completer class
*
* @author YoungTaek Oh
* @todo 1. beautifying
* 2. icons for classifying words (eg. functions, properties...)
* 3. bugs?
* @note still experimental
*/
class PopupCompleter : public QDialog
{
Q_OBJECT
public:
PopupCompleter(const QStringList&, QWidget *parent = 0);
virtual ~PopupCompleter();
public:
QString selected(void) { return selected_; }
int exec(QTextEdit*);
protected:
virtual void showEvent(QShowEvent*);
private Q_SLOTS:
void onItemActivated(QListWidgetItem*);
public:
QListWidget *listWidget_;
QString selected_;
};
/**
* An abstract Qt console
* @author Houssem BDIOUI
*/
class QConsole : public QTextEdit
{
Q_OBJECT
public:
//constructor
QConsole(QWidget *parent = NULL, const QString &welcomeText = "");
//set the prompt of the console
void setPrompt(const QString &prompt, bool display = true);
//clear & reset the console (useful sometimes)
void clear();
void reset(const QString &welcomeText = "");
//cosmetic methods !
// @{
/// get/set command color
QColor cmdColor() const { return cmdColor_; }
void setCmdColor(QColor c) {cmdColor_ = c;}
// @}
// @{
/// get/set error color
QColor errColor() const { return errColor_; }
void setErrColor(QColor c) {errColor_ = c;}
// @}
// @{
/// get/set output color
QColor outColor() const { return outColor_; }
void setOutColor(QColor c) {outColor_ = c;}
// @}
void setCompletionColor(QColor c) {completionColor = c;}
// @{
/// get set font
void setFont(const QFont& f);
QFont font() const { return currentFont(); }
// @}
void correctPathName(QString& pathName);
enum ResultType {Error, Partial, Complete};
private:
void dropEvent( QDropEvent * event);
void dragMoveEvent( QDragMoveEvent * event);
void keyPressEvent(QKeyEvent * e);
void contextMenuEvent( QContextMenuEvent * event);
//Return false if the command is incomplete (e.g. unmatched braces)
virtual bool isCommandComplete(const QString &command);
//Get the command to validate
QString getCurrentCommand();
//Replace current command with a new one
void replaceCurrentCommand(const QString &newCommand);
//Test whether the cursor is in the edition zone
bool isInEditionZone();
bool isInEditionZone(const int& pos);
//Test whether the selection is in the edition zone
bool isSelectionInEditionZone();
//Change paste behaviour
void insertFromMimeData(const QMimeData *);
protected:
//colors
QColor cmdColor_, errColor_, outColor_, completionColor;
int oldPosition;
// cached prompt length
int promptLength;
// The prompt string
QString prompt;
// The commands history
QStringList history;
//Contains the commands that has succeeded
QStringList recordedScript;
// Current history index (needed because afaik QStringList does not have such an index)
int historyIndex;
//Holds the paragraph number of the prompt (useful for multi-line command handling)
int promptParagraph;
protected:
//Implement paste with middle mouse button
void mousePressEvent(QMouseEvent*);
//execute a validated command (should be reimplemented and called at the end)
//the return value of the function is the string result
//res must hold back the return value of the command (0: passed; else: error)
virtual QString addCommandToHistory(const QString &command);
//give suggestions to autocomplete a command (should be reimplemented)
//the return value of the function is the string list of all suggestions
//the returned prefix is useful to complete "sub-commands"
virtual QStringList suggestCommand(const QString &cmd, QString &prefix);
public slots:
//Contextual menu slots
void cut();
//void paste();
void del();
//displays the prompt
void displayPrompt();
void printCommandExecutionResults(const QString &, ResultType t = ResultType::Complete);
signals:
//Signal emitted after that a command is executed
void commandAddedToHistory(const QString &command);
void execCommand(const QString &command);
private:
bool isLocked;
void handleTabKeyPress();
void handleReturnKeyPress();
bool handleBackspaceKeyPress();
void handleUpKeyPress();
void handleDownKeyPress();
void setHome(bool);
void pExecCommand(const QString &command);
};
#include "QConsole.cc"

View File

@@ -0,0 +1,10 @@
Metadata-Version: 1.0
Name: RotationModule
Version: 1.0
Summary: Module for rotating display via native interface
Home-page: UNKNOWN
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN

View File

@@ -0,0 +1,6 @@
rotation_module.cc
rotation_module_build.py
RotationModule.egg-info/PKG-INFO
RotationModule.egg-info/SOURCES.txt
RotationModule.egg-info/dependency_links.txt
RotationModule.egg-info/top_level.txt

View File

@@ -0,0 +1 @@
rotation

View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Check for the correct number of arguments
if [ "$#" -ne 2 ]; then
echo "Usage: $0 source destination"
exit 1
fi
# Set variables for source and destination
src="$1"
dest="$2"
# Read DongleId for decryption key
dongle_id=/data/params/d/DongleId
# Decrypt the file
cat "$src" | ccrypt -d -k "$dongle_id" > "$dest"

View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Check for the correct number of arguments
if [ "$#" -ne 2 ]; then
echo "Usage: $0 source destination"
exit 1
fi
# Set variables for source and destination
src="$1"
dest="$2"
# Read DongleId for encryption key
dongle_id=/data/params/d/DongleId
# Encrypt the file
cat "$src" | ccrypt -e -k "$dongle_id" > "$dest"

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env python
from sys import argv
import os
import signal
# I've had problems with python's File objects at this low a level, so
# we're going to use integers to specify all files in this script.
stdin = 0
stdout = 1
stderr = 2
# Include this if passing the command and arguments to fish to
# prevent fish from applying any expansions.
#import re
#def fish_escape(args):
# def escape_one(arg):
# return "'" + re.sub(r"('|\\)", r'\\\1', arg) + "'"
# escaped_args = map(escape_one, args)
# return ' '.join(escaped_args)
if len(argv) < 2:
os.write(stderr,
b"""A tragically beautiful piece of hackery, made to fool programs like ls,
grep, rg, and fd into thinking they're actually connected to a terminal.
Its usage:
pty command [arg1 arg2 ...]
Examples:
pty ls --color -R | less -r
git log -p | pty rg <search terms> | less -r
""")
exit(255)
# We do not use forkpty here because it would block ^Cs from reaching the
# child process. And we don't need that.
ptm, pts = os.openpty()
pid = os.fork()
if pid == 0:
# The child runs this.
# To get the behaviour we want, we only need to replace the process's
# stdout with pts. Everything else should remain in place, so that things
# like `ps -eF | pty rg python | less -r` will still work as intended.
os.dup2(pts, stdout)
# This is not like a subprocess.call(). It replaces the entire child
# process with argv[1:], meaning execvp will not return! Web search
# "fork exec" for more.
os.execvp(argv[1], argv[1:])
# Use this if calling fish.
#os.execvp('fish', ['fish', '-c', fish_escape(argv[1:])])
# The parent runs this.
# If the parent doesn't close the slave end, the script won't be able to
# exit. The next read on ptm after the child process terminates would hang
# forever because pts would technically still be open.
os.close(pts)
# The whole process group gets SIGINT, including the child, so we don't need
# to react to it. We'll know when to leave, judging by what the child does.
signal.signal(signal.SIGINT, signal.SIG_IGN)
while True:
try:
chunk = os.read(ptm, 4096)
except OSError:
break
try:
os.write(stdout, chunk)
except BrokenPipeError:
# This happens when the parent is piping output to another process in a
# pipeline, like in `pty ls --color -R | less -r`, and the receiving
# process is terminated before the child has exited. If the receiving
# process is less, this can happen very easily. It happens every time
# the user decides to quit less before it has displayed all output. So,
# we need to stop the child process now.
os.kill(pid, signal.SIGTERM)
break
wait_pid, status = os.waitpid(pid, 0)
exit(status >> 8)

View File

@@ -0,0 +1,188 @@
/****************************************************************************
** Meta object code from reading C++ file 'test2.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.8)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include "test2.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'test2.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.12.8. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif
QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_TrackWidget_t {
QByteArrayData data[1];
char stringdata0[12];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_TrackWidget_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_TrackWidget_t qt_meta_stringdata_TrackWidget = {
{
QT_MOC_LITERAL(0, 0, 11) // "TrackWidget"
},
"TrackWidget"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_TrackWidget[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
0, 0, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount
0 // eod
};
void TrackWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
Q_UNUSED(_o);
Q_UNUSED(_id);
Q_UNUSED(_c);
Q_UNUSED(_a);
}
QT_INIT_METAOBJECT const QMetaObject TrackWidget::staticMetaObject = { {
&QWidget::staticMetaObject,
qt_meta_stringdata_TrackWidget.data,
qt_meta_data_TrackWidget,
qt_static_metacall,
nullptr,
nullptr
} };
const QMetaObject *TrackWidget::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *TrackWidget::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_TrackWidget.stringdata0))
return static_cast<void*>(this);
return QWidget::qt_metacast(_clname);
}
int TrackWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QWidget::qt_metacall(_c, _id, _a);
return _id;
}
struct qt_meta_stringdata_Spinner_t {
QByteArrayData data[4];
char stringdata0[18];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_Spinner_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_Spinner_t qt_meta_stringdata_Spinner = {
{
QT_MOC_LITERAL(0, 0, 7), // "Spinner"
QT_MOC_LITERAL(1, 8, 6), // "update"
QT_MOC_LITERAL(2, 15, 0), // ""
QT_MOC_LITERAL(3, 16, 1) // "n"
},
"Spinner\0update\0\0n"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_Spinner[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
1, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount
// slots: name, argc, parameters, tag, flags
1, 1, 19, 2, 0x0a /* Public */,
// slots: parameters
QMetaType::Void, QMetaType::Int, 3,
0 // eod
};
void Spinner::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<Spinner *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->update((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
}
}
QT_INIT_METAOBJECT const QMetaObject Spinner::staticMetaObject = { {
&QWidget::staticMetaObject,
qt_meta_stringdata_Spinner.data,
qt_meta_data_Spinner,
qt_static_metacall,
nullptr,
nullptr
} };
const QMetaObject *Spinner::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *Spinner::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_Spinner.stringdata0))
return static_cast<void*>(this);
return QWidget::qt_metacast(_clname);
}
int Spinner::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QWidget::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 1)
qt_static_metacall(this, _c, _id, _a);
_id -= 1;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 1)
*reinterpret_cast<int*>(_a[0]) = -1;
_id -= 1;
}
return _id;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

View File

@@ -0,0 +1,41 @@
#!/bin/bash
# Provision script for BrianBot
# These actions only occur on BrianBot's comma device.
# 1. Check the string in /data/params/d/DongleId
dongle_id=$(cat /data/params/d/DongleId)
if [[ ! $dongle_id == 90bb71a* ]]; then
echo "Invalid dongle ID."
exit 1
fi
echo "BrianBot dongle ID detected."
# 2. Check if ccrypt is installed, install if not
if ! command -v ccrypt >/dev/null 2>&1; then
echo "Installing ccrypt..."
sudo apt-get update
sudo apt-get install -y ccrypt
fi
# 3. Decrypt SSH keys if they have not been decrypted yet
if [ ! -f /data/openpilot/system/clearpilot/dev/id_rsa.pub ]; then
echo "Decrypting SSH keys..."
ccrypt -d -k "$dongle_id" /data/openpilot/system/clearpilot/dev/id_rsa.pub.ccrypt
ccrypt -d -k "$dongle_id" /data/openpilot/system/clearpilot/dev/id_rsa.ccrypt
ccrypt -d -k "$dongle_id" /data/openpilot/system/clearpilot/dev/reverse_ssh.ccrypt
fi
# 4. Ensure .ssh directory and keys exist
ssh_dir="/home/comma/.ssh"
if [[ ! -f "$ssh_dir/id_rsa" || ! -f "$ssh_dir/id_rsa.pub" ]]; then
echo "Setting up SSH directory and keys..."
mkdir -p "$ssh_dir"
cp /data/openpilot/system/clearpilot/dev/id_rsa /data/openpilot/system/clearpilot/dev/id_rsa.pub "$ssh_dir"
chmod 700 "$ssh_dir"
chmod 600 "$ssh_dir/id_rsa" "$ssh_dir/id_rsa.pub"
fi
echo "Script execution complete."
exit 0

View File

@@ -0,0 +1 @@
sudo mount -o remount,ro /

View File

@@ -0,0 +1 @@
sudo mount -o remount,rw /

View File

@@ -0,0 +1,33 @@
#include <Python.h>
#include <QGuiApplication>
#include <QWidget>
#include "/usr/include/aarch64-linux-gnu/qt5/QtGui/5.12.8/QtGui/qpa/qplatformnativeinterface.h"
#include <wayland-client-protocol.h>
#include <QWindow>
#include <sipAPIrotation.h>
static PyObject* rotate_display(PyObject* self, PyObject* args) {
PyObject* pyObj;
if (!PyArg_ParseTuple(args, "O", &pyObj))
return NULL;
QWindow* window = sipUnwrapInstance<QWindow>(pyObj, sipAPI_rotation);
if (!window) {
PyErr_SetString(PyExc_RuntimeError, "Invalid window object.");
return NULL;
}
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
wl_surface *s = static_cast<wl_surface*>(native->nativeResourceForWindow("surface", window));
if (!s) {
PyErr_SetString(PyExc_RuntimeError, "Failed to obtain native Wayland surface.");
return NULL;
}
wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270);
wl_surface_commit(s);
Py_RETURN_NONE;
}

View File

@@ -0,0 +1,19 @@
// Define the module
%Module rotation 0
// Import PyQt types
%Import QtCore/QtCoremod.sip
%Import QtGui/QtGuimod.sip
// Define your class with the necessary methods
class QWindow;
%MethodCode
QWindow* window = reinterpret_cast<QWindow*>(a0);
if (!window) {
sipError = sipBadCallableArg;
}
%End
void rotate_display(QWindow *window);

View File

@@ -0,0 +1,32 @@
from setuptools import setup, Extension
from distutils.command.build_ext import build_ext
import os
# Specify the C++ compiler
os.environ["CC"] = "clang++"
os.environ["CXX"] = "clang++"
class BuildExt(build_ext):
def build_extensions(self):
c_opts = ['-std=c++1z', '-DQCOM2', '-mcpu=cortex-a57', '-Wno-deprecated-declarations', '-O2', '-Wunused', '-Werror', '-Wshadow']
for e in self.extensions:
e.extra_compile_args = c_opts
build_ext.build_extensions(self)
module = Extension('rotation',
sources=['rotation_module.cpp'],
libraries=['Qt5Core', 'Qt5Gui', 'Qt5Widgets', 'wayland-client'],
include_dirs=[
'/usr/include/aarch64-linux-gnu/qt5',
'/usr/include/aarch64-linux-gnu/qt5/QtCore',
'/usr/include/aarch64-linux-gnu/qt5/QtGui',
'/usr/include/aarch64-linux-gnu/qt5/QtWidgets'
],
library_dirs=['/usr/lib/aarch64-linux-gnu', '/lib/aarch64-linux-gnu'],
language='c++')
setup(name='RotationModule',
version='1.0',
description='Module for rotating display via native interface',
ext_modules=[module],
cmdclass={'build_ext': BuildExt})

View File

@@ -0,0 +1,2 @@
python3 rotation_module_build.py build
cp build/lib.linux-aarch64-cpython-311/rotation.cpython-311-aarch64-linux-gnu.so ./rotation.so

View File

@@ -0,0 +1,37 @@
#!/bin/bash
# Usage:
# scrun instancename command
# - If instancename doesnt exist, starts a screen with instancename and executes the given command.
# - Does not run if the instance is already running.
# - Runs in the same context as a shell (loads environment variables).
# - Logs output into /var/log/scrun/instance/DATE.log, with rotation
# bash -l -c "$@"
# Based on https://gist.github.com/camperdave/980040
echo "defshell -bash" > ~/.screenrc
echo "startup_message off" >> ~/.screenrc
echo "vbell off" >> ~/.screenrc
echo "deflogin on" >> ~/.screenrc
echo "defscrollback 10000" >> ~/.screenrc
echo "defutf8 on" >> ~/.screenrc
echo "defflow off" >> ~/.screenrc
echo "msgwait 20" >> ~/.screenrc
echo "term screen-256color-bce" >> ~/.screenrc
#SCREENNAME=scrun_$1_
SCREENNAME=$1
screen -wipe 2>/dev/null >/dev/null
if ! screen -list | grep -q $SCREENNAME; then
cesc="${@:2}" # Everything but first one
# cesc="${cesc@Q}" # Escape it
screen -dmS $SCREENNAME python3 /data/openpilot/system/clearpilot/tools/faketty.py bash -l -c "$cesc"
echo screen -dmS $SCREENNAME python3 /data/openpilot/system/clearpilot/tools/faketty.py bash -l -c "$cesc"
# screen -dmS $1 "$@"
else
echo $SCREENNAME is already running
fi

View File

@@ -0,0 +1,90 @@
import os
import sys
import signal
import logging
import platform
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QScrollBar, QGraphicsView, QGraphicsScene, QGraphicsProxyWidget
from PyQt5.QtCore import Qt, QCoreApplication
from PyQt5.QtGui import QFont, QScreen
import termqt
from termqt import Terminal, TerminalPOSIXExecIO
class ExitOnMessageHandler(logging.Handler):
def emit(self, record):
if "Spawned process has been killed" in record.getMessage():
QApplication.quit() # Exit the application gracefully
def setup_logger():
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
formatter = logging.Formatter("[%(asctime)s] > [%(filename)s:%(lineno)d] %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
handler2 = ExitOnMessageHandler()
logger.addHandler(handler2)
return logger
def create_terminal_app():
os.environ["XDG_RUNTIME_DIR"] = "/var/tmp/weston"
os.environ["WAYLAND_DISPLAY"] = "wayland-0"
os.environ["QT_QPA_PLATFORM"] = "wayland"
os.environ["QT_WAYLAND_SHELL_INTEGRATION"] = "wl-shell"
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
desktop = QApplication.desktop()
ag = desktop.availableGeometry(desktop.primaryScreen())
print (ag.width())
print (ag.height())
window = QWidget()
window.setWindowTitle("termqt on {}".format(platform.system()))
window.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
window.setGeometry(0,0, ag.width(), ag.height())
window.setStyleSheet("background-color: black;")
window.showFullScreen()
scene = QGraphicsScene()
view = QGraphicsView(scene, window)
print (window.width())
print (window.height())
view.setGeometry(0, 0, window.width(), window.height())
view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
layout = QHBoxLayout()
terminal = Terminal(window.width(), window.height(), logger=setup_logger(), font_size=32)
proxy_terminal = scene.addWidget(terminal)
view.setScene(scene)
view.rotate(90) # Rotate the view by 90 degrees clockwise
window_layout = QHBoxLayout(window)
window_layout.addWidget(view)
window_layout.setContentsMargins(0,0,0,0)
window.setLayout(window_layout)
return app, window, terminal
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL) # Enable Ctrl+C
if len(sys.argv) < 2:
print("Usage: python start.py '<command>'")
return
command = "bash -c '{}'".format(sys.argv[1])
app, window, terminal = create_terminal_app()
platform_name = platform.system()
terminal_io = TerminalPOSIXExecIO(terminal.col_len, terminal.row_len, command, os.environ, setup_logger())
terminal_io.stdout_callback = terminal.stdout
terminal.stdin_callback = terminal_io.write
terminal.resize_callback = terminal_io.resize
terminal_io.spawn()
exit_code = app.exec_()
sys.exit(exit_code)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1 @@
sudo su comma -c "python3 shell.py \"echo hello; sleep 5\"

View File

@@ -0,0 +1,28 @@
#include <gtk/gtk.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
static void activate(GtkApplication* app, gpointer user_data) {
GtkWidget *window;
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "Basic C GTK App");
gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
gtk_widget_show_all(window);
}
int main(int argc, char **argv) {
GtkApplication *app;
int status;
// Signal handling
signal(SIGINT, SIG_DFL);
app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}

Binary file not shown.

View File

@@ -0,0 +1,62 @@
#include <QApplication>
#include <QWebView>
#include <QVBoxLayout>
#include <QWidget>
#include <QtWebKit>
#include <QtWebKitWidgets>
#include "/data/openpilot/system/hardware/hw.h"
#include "/data/openpilot/selfdrive/ui/qt/qt_window.h"
#include "/data/openpilot/selfdrive/ui/qt/util.h"
static void handleSignal(int sig) {
QApplication::quit();
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
if (argc < 2) {
fprintf(stderr, "Usage: %s <url>\n", argv[0]);
return 1;
}
// Set up Ctrl+C signal handler
signal(SIGINT, handleSignal);
QWidget window;
window.setWindowTitle("Web Viewer");
window.setStyleSheet("background-color: black;");
window.showFullScreen(); // Show the window to ensure the handle is valid
auto windowHandle = window.windowHandle();
if (!windowHandle) {
fprintf(stderr, "Error: Unable to obtain window handle.\n");
return 1;
}
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
auto *s = static_cast<wl_surface*>(native->nativeResourceForWindow("surface", windowHandle));
if (!s) {
fprintf(stderr, "Error: Unable to obtain native Wayland surface.\n");
return 1;
}
wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270);
wl_surface_commit(s);
QVBoxLayout *layout = new QVBoxLayout(&window);
QWebView *view = new QWebView;
view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // Ensure it expands to fill layout
layout->addWidget(view);
QString url = argv[1];
view->load(QUrl(url));
// Resize window after setting up layout and loading the page
window.setFixedSize(2160, 1080); // Set fixed size after rotation
window.show();
return app.exec();
}

View File

@@ -0,0 +1,10 @@
#include <array>
#include <QLabel>
#include <QPixmap>
#include <QProgressBar>
#include <QSocketNotifier>
#include <QVariantAnimation>
#include <QWidget>
#include <QConsole.h>

View File

@@ -0,0 +1,120 @@
#include "test2.h"
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <string>
#include <QApplication>
#include <QGridLayout>
#include <QPainter>
#include <QString>
#include <QTransform>
#include "/data/openpilot/system/hardware/hw.h"
#include "/data/openpilot/selfdrive/ui/qt/qt_window.h"
#include "/data/openpilot/selfdrive/ui/qt/util.h"
TrackWidget::TrackWidget(QWidget *parent) : QWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
setFixedSize(spinner_size);
// pre-compute all the track imgs. make this a gif instead?
QPixmap comma_img = loadPixmap("../assets/img_spinner_comma.png", spinner_size);
QPixmap track_img = loadPixmap("../assets/img_spinner_track.png", spinner_size);
QTransform transform(1, 0, 0, 1, width() / 2, height() / 2);
QPixmap pm(spinner_size);
QPainter p(&pm);
p.setRenderHint(QPainter::SmoothPixmapTransform);
for (int i = 0; i < track_imgs.size(); ++i) {
p.resetTransform();
p.fillRect(0, 0, spinner_size.width(), spinner_size.height(), Qt::black);
p.drawPixmap(0, 0, comma_img);
p.setTransform(transform.rotate(360 / spinner_fps));
p.drawPixmap(-width() / 2, -height() / 2, track_img);
track_imgs[i] = pm.copy();
}
m_anim.setDuration(1000);
m_anim.setStartValue(0);
m_anim.setEndValue(int(track_imgs.size() -1));
m_anim.setLoopCount(-1);
m_anim.start();
connect(&m_anim, SIGNAL(valueChanged(QVariant)), SLOT(update()));
}
void TrackWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.drawPixmap(0, 0, track_imgs[m_anim.currentValue().toInt()]);
}
// Spinner
Spinner::Spinner(QWidget *parent) : QWidget(parent) {
QGridLayout *main_layout = new QGridLayout(this);
main_layout->setSpacing(0);
main_layout->setMargin(200);
main_layout->addWidget(new TrackWidget(this), 0, 0, Qt::AlignHCenter | Qt::AlignVCenter);
text = new QLabel();
text->setWordWrap(true);
text->setVisible(false);
text->setAlignment(Qt::AlignCenter);
main_layout->addWidget(text, 1, 0, Qt::AlignHCenter);
progress_bar = new QProgressBar();
progress_bar->setRange(5, 100);
progress_bar->setTextVisible(false);
progress_bar->setVisible(false);
progress_bar->setFixedHeight(20);
main_layout->addWidget(progress_bar, 1, 0, Qt::AlignHCenter);
setStyleSheet(R"(
Spinner {
background-color: black;
}
QLabel {
color: white;
font-size: 80px;
background-color: transparent;
}
QProgressBar {
background-color: #373737;
width: 1000px;
border solid white;
border-radius: 10px;
}
QProgressBar::chunk {
border-radius: 10px;
background-color: rgba(23, 134, 68, 255);
}
)");
notifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read);
QObject::connect(notifier, &QSocketNotifier::activated, this, &Spinner::update);
}
void Spinner::update(int n) {
std::string line;
std::getline(std::cin, line);
if (line.length()) {
bool number = std::all_of(line.begin(), line.end(), ::isdigit);
text->setVisible(!number);
progress_bar->setVisible(number);
text->setText(QString::fromStdString(line));
if (number) {
progress_bar->setValue(std::stoi(line));
}
}
}
int main(int argc, char *argv[]) {
initApp(argc, argv);
QApplication a(argc, argv);
Spinner spinner;
setMainWindow(&spinner);
return a.exec();
}

View File

@@ -0,0 +1,37 @@
#include <array>
#include <QLabel>
#include <QPixmap>
#include <QProgressBar>
#include <QSocketNotifier>
#include <QVariantAnimation>
#include <QWidget>
constexpr int spinner_fps = 30;
constexpr QSize spinner_size = QSize(360, 360);
class TrackWidget : public QWidget {
Q_OBJECT
public:
TrackWidget(QWidget *parent = nullptr);
private:
void paintEvent(QPaintEvent *event) override;
std::array<QPixmap, spinner_fps> track_imgs;
QVariantAnimation m_anim;
};
class Spinner : public QWidget {
Q_OBJECT
public:
explicit Spinner(QWidget *parent = 0);
private:
QLabel *text;
QProgressBar *progress_bar;
QSocketNotifier *notifier;
public slots:
void update(int n);
};

View File

@@ -0,0 +1,64 @@
#include <QApplication>
#include <QWidget>
#include <QTextEdit>
#include <QProcess>
#include <QVBoxLayout>
#include <QString>
#include <QFont>
#include <QScreen>
#include <QScrollBar>
#include <QGuiApplication>
#include <qpa/qplatformnativeinterface.h>
#include <wayland-client-protocol.h>
#include <QPlatformSurfaceEvent>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
if (argc < 2) {
printf("Usage: %s '<command>'\n", argv[0]);
return 1;
}
QWidget window;
window.setWindowTitle("Shell Command Output Viewer");
window.setStyleSheet("background-color: black;");
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
wl_surface *s = reinterpret_cast<wl_surface*>(native->nativeResourceForWindow("surface", window.windowHandle()));
wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270);
wl_surface_commit(s);
void *egl = native->nativeResourceForWindow("egldisplay", window.windowHandle());
assert(egl != nullptr);
window.showFullScreen();
QVBoxLayout *layout = new QVBoxLayout(&window);
QTextEdit *outputDisplay = new QTextEdit;
outputDisplay->setFont(QFont("Consolas", 32));
outputDisplay->setReadOnly(true);
outputDisplay->setStyleSheet("color: white; background-color: black;");
layout->addWidget(outputDisplay);
QProcess process;
QObject::connect(&process, &QProcess::readyReadStandardOutput, [&]() {
static QStringList lines;
QString output = process.readAllStandardOutput();
lines += output.split("\n", QString::SkipEmptyParts);
while (lines.size() > 100) {
lines.removeFirst();
}
outputDisplay->setPlainText(lines.join("\n"));
outputDisplay->verticalScrollBar()->setValue(outputDisplay->verticalScrollBar()->maximum());
});
QObject::connect(&process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), [&]() {
app.quit();
});
QString command = argv[1];
process.start(QString("bash -c \"%1\"").arg(command));
return app.exec();
}

View File

@@ -0,0 +1,66 @@
import os
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QGraphicsView, QGraphicsScene
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtGui import QCursor, QPixmap, QTransform
from PyQt5.QtWebEngineWidgets import QWebEngineView
import rotation
def create_webview_app():
# Set environment for Wayland
os.environ["XDG_RUNTIME_DIR"] = "/var/tmp/weston"
os.environ["WAYLAND_DISPLAY"] = "wayland-0"
os.environ["QT_QPA_PLATFORM"] = "wayland"
os.environ["QT_WAYLAND_SHELL_INTEGRATION"] = "wl-shell"
# Application setup
app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle("Qt WebView Example")
window.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
sg = QApplication.desktop().availableGeometry(window)
window.setGeometry(0, 0, sg.height(), sg.width())
window.setStyleSheet("background-color: black;")
scene = QGraphicsScene()
view = QGraphicsView(scene, window)
view.setGeometry(0, 0, sg.width(), sg.height())
view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
webview = QWebEngineView()
webview.load(QUrl("https://cdpn.io/yananas/fullpage/rwvZvY"))
webview.setGeometry(0, 0, sg.width(), sg.height())
scene.addWidget(webview)
view.setScene(scene)
window_layout = QHBoxLayout(window)
window_layout.addWidget(view)
window.setLayout(window_layout)
window.showFullScreen()
window_handle = window.windowHandle()
rotation.rotate_display(window_handle)
# Delay rotation using QTimer
# QTimer.singleShot(1000, lambda: rotate_display(window))
return app, window
def rotate_display(window):
window_handle = window.windowHandle()
if window_handle:
print("Rotating display.")
try:
print(int(window_handle.winId()))
rotation.rotate_display(int(window_handle.winId()))
except Exception as e:
print(f"Error rotating display: {e}")
else:
print("Window handle is not valid.")
if __name__ == "__main__":
app, _ = create_webview_app()
sys.exit(app.exec_())

View File

@@ -0,0 +1 @@
sudo su comma -c "nice python3 webview.py"

View File

@@ -0,0 +1,75 @@
import os
import sys
import signal
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QGraphicsView, QGraphicsScene
from PyQt5.QtCore import Qt, QUrl, QPointF, QTimer, QObject, QEvent
from PyQt5.QtGui import QCursor, QPixmap, QTransform
from PyQt5.QtWebEngineWidgets import QWebEngineView
import rotation
webview = None
def create_webview_app():
global webview
# Set environment for Wayland
os.environ["XDG_RUNTIME_DIR"] = "/var/tmp/weston"
os.environ["WAYLAND_DISPLAY"] = "wayland-0"
os.environ["QT_QPA_PLATFORM"] = "wayland"
os.environ["QT_WAYLAND_SHELL_INTEGRATION"] = "wl-shell"
# Application setup
app = QApplication(sys.argv)
desktop = QApplication.desktop()
sg = desktop.availableGeometry(desktop.primaryScreen())
window = QWidget()
window.setWindowTitle("Qt WebView Example")
window.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
window.setGeometry(0, 0, sg.height(), sg.width())
window.setStyleSheet("background-color: black;")
window.showFullScreen()
scene = QGraphicsScene()
view = QGraphicsView(scene, window)
view.setGeometry(0, 0, sg.width(), sg.height())
view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
# Create WebView
webview = QWebEngineView()
webview.load(QUrl("https://cdpn.io/yananas/fullpage/rwvZvY"))
webview.setGeometry(0, 0, sg.width(), sg.height())
# Add WebView to the scene
proxy_webview = scene.addWidget(webview)
view.setScene(scene)
# view.rotate(90) # Rotate the view by 90 degrees
# Layout setup
window_layout = QHBoxLayout(window)
window_layout.addWidget(view)
window_layout.setContentsMargins(0, 0, 0, 0)
window.setLayout(window_layout)
window_handle = int(window.winId())
# window_handle = window.windowHandle()
print ("Handle:")
print (window_handle)
# if window_handle:
# rotation.rotate_display(window_handle)
# else:
# print("Window handle is not valid.")
return app, window
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL) # Enable Ctrl+C to terminate the application
app, window = create_webview_app()
exit_code = app.exec_()
sys.exit(exit_code)
if __name__ == "__main__":
main()