Commit d691a3d7 authored by Matan Ziv-Av's avatar Matan Ziv-Av Committed by Tomaz Canabrava
Browse files

Add infrastructure for displaying graphics in terminal

This allows for showing graphics in the terminal.
konsole keeps two lists of graphics to display (called placements).
Each placement includes:
- A pixmap to display.
- Column and row where the image is displayed.
- A z index. The text has z=0, so placements with z<0 are displayed below the text.
- An opacity value with which the pixmap is drawn.
- Whether the graphics scrolls with the text

Graphics placements with pid >= 0 are managed by the application, which can remove them. They need to have a unique <id,pid> pair.
Placements with pid<0 are not managed by the application. They are deleted automatically when they are completely obscured by others.
parent 5f57601a
......@@ -790,6 +790,11 @@ void Screen::reset()
initTabStops();
setDefaultRendition();
saveCursor();
if (_currentTerminalDisplay && _currentTerminalDisplay->hasGraphics()) {
_currentTerminalDisplay->delPlacements();
_currentTerminalDisplay->update();
}
}
void Screen::backspace()
......@@ -1051,6 +1056,8 @@ void Screen::scrollUp(int from, int n)
// FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
moveImage(loc(0, from), loc(0, from + n), loc(_columns, _bottomMargin));
clearImage(loc(0, _bottomMargin - n + 1), loc(_columns - 1, _bottomMargin), ' ');
if (_currentTerminalDisplay->hasGraphics())
_currentTerminalDisplay->scrollUpVisiblePlacements(n);
}
void Screen::scrollDown(int n)
......
......@@ -1619,6 +1619,10 @@ void Vt102Emulation::setMode(int m)
_screen[1]->setDefaultRendition();
_screen[1]->clearSelection();
setScreen(1);
if (_currentScreen->currentTerminalDisplay()) { // We may get here before we have a TerminalDisplay
_currentScreen->currentTerminalDisplay()->selectPlacements(1);
_currentScreen->currentTerminalDisplay()->delPlacements(1);
}
break;
}
// FIXME: Currently this has a redundant condition as MODES_SCREEN is 6
......@@ -1661,6 +1665,8 @@ void Vt102Emulation::resetMode(int m)
case MODE_AppScreen:
_screen[0]->clearSelection();
setScreen(0);
if (_currentScreen->currentTerminalDisplay()) // We may get here before we have a TerminalDisplay
_currentScreen->currentTerminalDisplay()->selectPlacements(0);
break;
}
// FIXME: Currently this has a redundant condition as MODES_SCREEN is 7
......
......@@ -75,6 +75,7 @@
#include "TerminalColor.h"
#include "TerminalFonts.h"
#include "TerminalGraphics.h"
#include "TerminalPainter.h"
#include "TerminalScrollBar.h"
......@@ -358,6 +359,12 @@ TerminalDisplay::TerminalDisplay(QWidget *parent)
};
_printManager.reset(new KonsolePrintManager(ldrawBackground, ldrawContents, lgetBackgroundColor));
_graphicsImages = std::map<int, QImage *>();
_graphicsPlacementsArray[0] = QVector<TerminalGraphicsPlacement_t *>();
_graphicsPlacementsArray[1] = QVector<TerminalGraphicsPlacement_t *>();
_graphicsPlacements = &_graphicsPlacementsArray[0];
_hasGraphics = false;
}
TerminalDisplay::~TerminalDisplay()
......@@ -367,6 +374,9 @@ TerminalDisplay::~TerminalDisplay()
delete[] _image;
delete _filterChain;
for (int p = 0; p < 2; p++)
for (int i = 0; i < _graphicsPlacementsArray[p].size(); i++)
delete _graphicsPlacementsArray[p][i];
}
void TerminalDisplay::setupHeaderVisibility()
......@@ -672,7 +682,11 @@ void TerminalDisplay::updateImage()
_screenWindow->resetScrollCount();
// update the parts of the display which have changed
update(dirtyRegion);
if (_hasGraphics) {
update();
} else {
update(dirtyRegion);
}
if (_allowBlinkingText && _hasTextBlinker && !_blinkTextTimer->isActive()) {
_blinkTextTimer->start();
......@@ -2937,3 +2951,108 @@ int TerminalDisplay::selectionState() const
{
return _actSel;
}
void TerminalDisplay::addPlacement(TerminalGraphicsPlacement_t *p)
{
int i;
// remove placement with the same id and pid, if pid is non zero
if (p->pid >= 0 && p->id >= 0)
for (i = 0; i < _graphicsPlacements->size(); i++)
if (p->id == (*_graphicsPlacements)[i]->id && p->pid == (*_graphicsPlacements)[i]->pid) {
_graphicsPlacements->remove(i);
break;
}
for (i = 0; i < _graphicsPlacements->size() && p->z >= (*_graphicsPlacements)[i]->z; i++)
;
_graphicsPlacements->insert(i, p);
_hasGraphics = true;
// Placements with pid<0 cannot be deleted, so remove those fully covered
// by others.
QRegion covered = QRegion();
for (int i = _graphicsPlacements->size() - 1; i >= 0; i--) {
TerminalGraphicsPlacement_t *placement = (*_graphicsPlacements)[i];
if (placement->pid < 0) {
QRect rect(placement->col, placement->row, placement->cols, placement->rows);
if (covered.contains(rect)) {
_graphicsPlacements->remove(i);
delete placement;
} else {
covered += rect;
}
}
}
}
TerminalGraphicsPlacement_t *TerminalDisplay::getGraphicsPlacement(int i)
{
if (i >= _graphicsPlacements->size())
return NULL;
return (*_graphicsPlacements)[i];
}
void TerminalDisplay::scrollUpVisiblePlacements(int n)
{
for (int i = _graphicsPlacements->size() - 1; i >= 0; i--) {
if ((*_graphicsPlacements)[i]->scrolling)
(*_graphicsPlacements)[i]->row -= n;
}
}
void TerminalDisplay::delPlacements(int del, qint64 id, qint64 pid, int x, int y, int z)
{
for (int i = _graphicsPlacements->size() - 1; i >= 0; i--) {
TerminalGraphicsPlacement_t *placement = (*_graphicsPlacements)[i];
bool remove = false;
switch (del) {
case 1:
remove = true;
break;
case 'z':
if (placement->z == z) {
remove = true;
}
break;
case 'x':
if (placement->col <= x && x < placement->col + placement->cols) {
remove = true;
}
break;
case 'y':
if (placement->row <= y && y < placement->row + placement->rows) {
remove = true;
}
break;
case 'p':
if (placement->col <= x && x < placement->col + placement->cols && placement->row <= y && y < placement->row + placement->rows) {
remove = true;
}
break;
case 'q':
if (placement->col <= x && x < placement->col + placement->cols && placement->row <= y && y < placement->row + placement->rows
&& placement->z == z) {
remove = true;
}
break;
case 'a':
if (placement->row + placement->rows > 0) {
remove = true;
}
break;
case 'i':
if (placement->id == id && (pid < 0 || placement->pid == pid)) {
remove = true;
}
break;
}
if (remove) {
_graphicsPlacements->remove(i);
delete placement;
}
}
}
void TerminalDisplay::selectPlacements(int i)
{
_graphicsPlacements = &_graphicsPlacementsArray[i ? 1 : 0];
}
......@@ -25,6 +25,7 @@
#include "widgets/TerminalHeaderBar.h"
#include "TerminalBell.h"
#include "TerminalGraphics.h"
class QDrag;
class QDragEnterEvent;
......@@ -383,6 +384,18 @@ public:
// Used to show/hide the message widget
void updateReadOnlyState(bool readonly);
void addPlacement(TerminalGraphicsPlacement_t *p);
TerminalGraphicsPlacement_t *getGraphicsPlacement(int i);
void scrollUpVisiblePlacements(int n);
void delPlacements(int = 'a', qint64 = 0, qint64 = -1, int = 0, int = 0, int = 0);
void selectPlacements(int i);
bool hasGraphics() const
{
return _hasGraphics;
}
public Q_SLOTS:
/**
* Causes the terminal display to fetch the latest character image from the associated
......@@ -772,6 +785,11 @@ private:
std::unique_ptr<TerminalFont> _terminalFont;
std::unique_ptr<KonsolePrintManager> _printManager;
std::map<int, QImage *> _graphicsImages;
QVector<TerminalGraphicsPlacement_t *> _graphicsPlacementsArray[2];
QVector<TerminalGraphicsPlacement_t *> *_graphicsPlacements;
bool _hasGraphics;
};
}
......
/*
SPDX-FileCopyrightText: 2022
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QPixmap>
struct TerminalGraphicsPlacement_t {
QPixmap pixmap;
qint64 id;
qint64 pid;
int z, X, Y, col, row, cols, rows;
qreal opacity;
bool scrolling;
};
......@@ -419,6 +419,29 @@ void TerminalPainter::drawTextFragment(QPainter &painter,
drawBackground(painter, rect, backgroundColor, false);
}
const auto display = qobject_cast<TerminalDisplay *>(sender());
int placementIdx = 0;
qreal opacity = painter.opacity();
int scrollDelta = display->terminalFont()->fontHeight() * (display->screenWindow()->currentLine() - display->screenWindow()->screen()->getHistLines());
const bool origClipping = painter.hasClipping();
const auto origClipRegion = painter.clipRegion();
if (display->hasGraphics()) {
painter.setClipRect(rect);
while (1) {
TerminalGraphicsPlacement_t *p = display->getGraphicsPlacement(placementIdx);
if (!p || p->z >= 0)
break;
int x = p->col * display->terminalFont()->fontWidth() + p->X;
int y = p->row * display->terminalFont()->fontHeight() + p->Y;
QRectF srcRect(0, 0, p->pixmap.width(), p->pixmap.height());
QRectF dstRect(x, y - scrollDelta, p->pixmap.width(), p->pixmap.height());
painter.setOpacity(p->opacity);
painter.drawPixmap(dstRect, p->pixmap, srcRect);
placementIdx++;
}
painter.setOpacity(opacity);
}
QColor characterColor = foregroundColor;
if ((style.rendition & RE_CURSOR) != 0) {
drawCursor(painter, rect, foregroundColor, backgroundColor, characterColor);
......@@ -426,6 +449,25 @@ void TerminalPainter::drawTextFragment(QPainter &painter,
// draw text
drawCharacters(painter, rect, text, style, characterColor, lineProperty);
if (display->hasGraphics()) {
while (1) {
TerminalGraphicsPlacement_t *p = display->getGraphicsPlacement(placementIdx);
if (!p)
break;
QPixmap image = p->pixmap;
int x = p->col * display->terminalFont()->fontWidth() + p->X;
int y = p->row * display->terminalFont()->fontHeight() + p->Y;
QRectF srcRect(0, 0, image.width(), image.height());
QRectF dstRect(x, y - scrollDelta, image.width(), image.height());
painter.setOpacity(p->opacity);
painter.drawPixmap(dstRect, image, srcRect);
placementIdx++;
}
painter.setOpacity(opacity);
painter.setClipRegion(origClipRegion);
painter.setClipping(origClipping);
}
}
void TerminalPainter::drawPrinterFriendlyTextFragment(QPainter &painter,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment