#include "video_grid_widget.h" #include "../media/screen_capture.h" #include "../connection/client_connection.h" #include "../../shared/protocol/message.h" #include #include #include #include namespace scar { VideoGridWidget::VideoGridWidget(QWidget* parent) : QWidget(parent), isScreenSharing_(false), connection_(nullptr) { gridLayout_ = new QGridLayout(this); gridLayout_->setSpacing(5); gridLayout_->setContentsMargins(5, 5, 5, 5); // Placeholder when no streams placeholderLabel_ = new QLabel("No active video streams", this); placeholderLabel_->setAlignment(Qt::AlignCenter); placeholderLabel_->setStyleSheet("color: #888; font-size: 16px;"); gridLayout_->addWidget(placeholderLabel_, 0, 0); // Create screen share button (floating above video streams) screenShareButton_ = new QPushButton("Share Screen", this); screenShareButton_->setStyleSheet( "QPushButton {" " background-color: #5865F2;" " color: white;" " border: none;" " border-radius: 8px;" " padding: 12px 24px;" " font-size: 14px;" " font-weight: bold;" "}" "QPushButton:hover {" " background-color: #4752C4;" "}" "QPushButton:pressed {" " background-color: #3C45A5;" "}" "QPushButton:disabled {" " background-color: #4F545C;" " color: #72767D;" "}" ); screenShareButton_->setCursor(Qt::PointingHandCursor); screenShareButton_->setFixedSize(160, 48); screenShareButton_->raise(); // Ensure button is above other widgets connect(screenShareButton_, &QPushButton::clicked, this, &VideoGridWidget::onScreenShareClicked); // Initialize screen capture screenCapture_ = new ScreenCapture(); updateScreenShareButtonPosition(); } VideoGridWidget::~VideoGridWidget() { delete screenCapture_; } void VideoGridWidget::setConnection(ClientConnection* connection) { connection_ = connection; // Set up frame callback for screen capture if (screenCapture_) { screenCapture_->setFrameCallback([this](const std::vector& frameData, int width, int height) { if (!connection_) { return; } try { static int callback_count = 0; if (++callback_count % 30 == 0) { std::cout << "Sending screen share frame: " << callback_count << " Size: " << frameData.size() << " bytes" << " Resolution: " << width << "x" << height << std::endl; } // Send frame to server ScreenShareData frameMsg(frameData); auto serialized = frameMsg.serialize(); connection_->sendData(serialized); } catch (const std::exception& e) { std::cerr << "Error in frame callback: " << e.what() << std::endl; } catch (...) { std::cerr << "Unknown error in frame callback" << std::endl; } }); } } void VideoGridWidget::addStream(const QString& streamId, const QString& username) { if (streams_.size() >= MAX_STREAMS) { return; // Max streams reached } // Remove placeholder if this is first stream if (streams_.empty()) { placeholderLabel_->hide(); } VideoStream stream; stream.streamId = streamId; stream.username = username; // Create video label stream.videoLabel = new QLabel(this); stream.videoLabel->setMinimumSize(160, 120); stream.videoLabel->setStyleSheet("QLabel { background-color: #2C2F33; border: 1px solid #23272A; }"); stream.videoLabel->setScaledContents(true); stream.videoLabel->setAlignment(Qt::AlignCenter); stream.videoLabel->setText(username + "\n(No video)"); stream.videoLabel->setToolTip(username); streams_.push_back(stream); updateGridLayout(); } void VideoGridWidget::removeStream(const QString& streamId) { for (auto it = streams_.begin(); it != streams_.end(); ++it) { if (it->streamId == streamId) { delete it->videoLabel; streams_.erase(it); break; } } if (streams_.empty()) { placeholderLabel_->show(); } else { updateGridLayout(); } } void VideoGridWidget::updateFrame(const QString& streamId, const QPixmap& frame) { for (auto& stream : streams_) { if (stream.streamId == streamId) { stream.videoLabel->setPixmap(frame); break; } } } void VideoGridWidget::clear() { for (auto& stream : streams_) { delete stream.videoLabel; } streams_.clear(); placeholderLabel_->show(); } void VideoGridWidget::updateGridLayout() { // Remove all widgets from layout for (auto& stream : streams_) { gridLayout_->removeWidget(stream.videoLabel); } int columns = calculateColumns(streams_.size()); int rows = std::ceil(static_cast(streams_.size()) / columns); // Re-add widgets in grid for (size_t i = 0; i < streams_.size(); ++i) { int row = i / columns; int col = i % columns; gridLayout_->addWidget(streams_[i].videoLabel, row, col); } } int VideoGridWidget::calculateColumns(int streamCount) { if (streamCount <= 1) return 1; if (streamCount <= 4) return 2; if (streamCount <= 9) return 3; if (streamCount <= 16) return 4; if (streamCount <= 25) return 5; if (streamCount <= 36) return 6; return std::ceil(std::sqrt(streamCount)); } void VideoGridWidget::onScreenShareClicked() { if (isScreenSharing_) { // Stop screen sharing screenCapture_->stop(); isScreenSharing_ = false; screenShareButton_->setText("Share Screen"); screenShareButton_->setStyleSheet( "QPushButton {" " background-color: #5865F2;" " color: white;" " border: none;" " border-radius: 8px;" " padding: 12px 24px;" " font-size: 14px;" " font-weight: bold;" "}" "QPushButton:hover {" " background-color: #4752C4;" "}" "QPushButton:pressed {" " background-color: #3C45A5;" "}" ); // Send stop message if (connection_) { ScreenShareStop stopMsg; auto serialized = stopMsg.serialize(); connection_->sendData(serialized); qDebug() << "Sent screen share stop message"; } } else { // Start screen sharing - this will open xdg-desktop-portal dialog if (screenCapture_->start()) { isScreenSharing_ = true; screenShareButton_->setText("Stop Sharing"); screenShareButton_->setStyleSheet( "QPushButton {" " background-color: #ED4245;" " color: white;" " border: none;" " border-radius: 8px;" " padding: 12px 24px;" " font-size: 14px;" " font-weight: bold;" "}" "QPushButton:hover {" " background-color: #C03537;" "}" "QPushButton:pressed {" " background-color: #A12D2F;" "}" ); // Send start message with resolution // Note: We'll get the actual resolution in the first frame callback if (connection_) { ScreenShareStart startMsg(1920, 1080); // Placeholder, real resolution comes from PipeWire auto serialized = startMsg.serialize(); connection_->sendData(serialized); qDebug() << "Sent screen share start message"; } emit screenShareRequested(); } } } void VideoGridWidget::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); updateScreenShareButtonPosition(); } void VideoGridWidget::updateScreenShareButtonPosition() { // Position button at bottom center of the widget int buttonWidth = screenShareButton_->width(); int buttonHeight = screenShareButton_->height(); int x = (width() - buttonWidth) / 2; int y = height() - buttonHeight - 20; // 20px margin from bottom screenShareButton_->move(x, y); } } // namespace scar