<commit_before>/*
    FLTKKeyboard.cpp:

    Copyright (C) 2006 Steven Yi

    This file is part of Csound.

    The Csound Library is free software; you can redistribute it
    and/or modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    Csound 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Csound; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA
*/

#include <iostream>

#include <FL/fl_draw.H>
#include "FLTKKeyboard.hpp"

FLTKKeyboard::FLTKKeyboard(CSOUND *csound, int X, int Y, int W, int H, const char *L)
	: Fl_Widget(X, Y, W, H, L) {

    this->csound = csound;
    this->mutex = csound->Create_Mutex(0);

  	FLTKKeyboard *o = this;

	o->box(FL_FLAT_BOX);
	o->color(FL_BACKGROUND_COLOR);
	o->selection_color(FL_BACKGROUND_COLOR);
	o->labeltype(FL_NO_LABEL);
	o->labelfont(0);
	o->labelsize(14);
	o->labelcolor(FL_FOREGROUND_COLOR);
	o->user_data((void*)(this));
	o->align(FL_ALIGN_TOP);
	o->when(FL_WHEN_RELEASE);

	for(int i = 0; i < 88; i++) {
		keyStates[i] = 0;
		changedKeyStates[i] = 0;
	}

	lastMidiKey = -1;

	whiteKeys[0] = 0;
	whiteKeys[1] = 2;
	whiteKeys[2] = 4;
	whiteKeys[3] = 5;
	whiteKeys[4] = 7;
	whiteKeys[5] = 9;
	whiteKeys[6] = 11;

	octave = 5;

	aNotesOff = 0;
}

FLTKKeyboard::~FLTKKeyboard() {
	if (mutex) {
      	csound->DestroyMutex(mutex);
      	mutex = (void*) 0;
    }
}

void FLTKKeyboard::lock() {
	if(mutex) {
      	csound->LockMutex(mutex);
	}
}

void FLTKKeyboard::unlock() {
	if(mutex) {
      	csound->UnlockMutex(mutex);
	}
}

void FLTKKeyboard::allNotesOff() {
	this->lock();

	for(int i = 0; i < 88; i++) {
		this->keyStates[i] = -1;
	}

	this->lastMidiKey = -1;

	this->aNotesOff = 1;

	this->unlock();

	this->redraw();
}

int FLTKKeyboard::getMidiValForWhiteKey(int whiteKeyNum) {
    if(whiteKeyNum < 2) {
        return whiteKeyNum * 2;
    }

    int adjusted = whiteKeyNum - 2;

    int oct = adjusted / 7;
    int key = adjusted % 7;

    return 3 + (oct * 12) + whiteKeys[key];
}

int FLTKKeyboard::getMIDIKey(int x, int y) {
	if(x > this->w()) {
		return 87;
	}

	if(x < 0) {
		return 0;
	}

    // 52 white keys
    int whiteKey = x  / whiteKeyWidth;

    int extra = x % whiteKeyWidth;

    if(whiteKey < 2) {
        if(whiteKey == 0) {
            if(y > blackKeyHeight) {
                return whiteKey;
            } else {
                if(extra > rightKeyBound) {
                    return whiteKey + 1;
                }
                return whiteKey;
            }
        } else {
            if(y > blackKeyHeight) {
                return getMidiValForWhiteKey(whiteKey);
            } else {
                if(extra < leftKeyBound) {
                    return getMidiValForWhiteKey(whiteKey) - 1;
                }
                return getMidiValForWhiteKey(whiteKey);
            }
        }
    }

    int adjustedKey = (whiteKey - 2) % 7;

    if(adjustedKey == 0 || adjustedKey == 3) {

        if(y > blackKeyHeight) {
            return getMidiValForWhiteKey(whiteKey);
        } else {
            if(extra > rightKeyBound) {
                return getMidiValForWhiteKey(whiteKey) + 1;
            }
            return getMidiValForWhiteKey(whiteKey);
        }
    } else if(adjustedKey == 2 || adjustedKey == 6) {
        if(y > blackKeyHeight) {
            return getMidiValForWhiteKey(whiteKey);
        } else {
            if(extra < leftKeyBound) {
                return getMidiValForWhiteKey(whiteKey) - 1;
            }
            return getMidiValForWhiteKey(whiteKey);
        }
    }

    if(y > blackKeyHeight) {
        return getMidiValForWhiteKey(whiteKey);
    }

    if(extra < leftKeyBound) {
        return getMidiValForWhiteKey(whiteKey) - 1;
    }

    if(extra > rightKeyBound) {
        return getMidiValForWhiteKey(whiteKey) + 1;
    }

    return getMidiValForWhiteKey(whiteKey);

}

void FLTKKeyboard::handleKey(int key, int value) {
	int index = -1;

	switch(key) {
		case 'z':
		    index = 0;
		    break;
		case 's':
		    index = 1;
		    break;
		case 'x':
		    index = 2;
		    break;
		case 'd':
		    index = 3;
		    break;
		case 'c':
		    index = 4;
		    break;
		case 'v':
		    index = 5;
		    break;
		case 'g':
		    index = 6;
		    break;
		case 'b':
		    index = 7;
		    break;
		case 'h':
		    index = 8;
		    break;
		case 'n':
		    index = 9;
		    break;
		case 'j':
		    index = 10;
		    break;
		case 'm':
		    index = 11;
		    break;
		case 'q':
		    index = 12;
		    break;
		case '2':
		    index = 13;
		    break;
		case 'w':
		    index = 14;
		    break;
		case '3':
		    index = 15;
		    break;
		case 'e':
		    index = 16;
		    break;
		case 'r':
		    index = 17;
		    break;
		case '5':
		    index = 18;
		    break;
		case 't':
		    index = 19;
		    break;
		case '6':
		    index = 20;
		    break;
		case 'y':
		    index = 21;
		    break;
		case '7':
		    index = 22;
		    break;
		case 'u':
		    index = 23;
		    break;
		case 'i':
		    index = 24;
		    break;
		case '9':
		    index = 25;
		    break;
		case 'o':
		    index = 26;
		    break;
		case '0':
		    index = 27;
		    break;
		case 'p':
		    index = 28;
		    break;
		default:
			return;
	}

	if(index < 0) {
		return;
	}

	index = ((octave * 12) + index) - 21;

	if(index < 0 || index > 87) {
		return;
	}

	lock();
	if(keyStates[index] != value) {
		keyStates[index] = value;
	}
	unlock();
}

int FLTKKeyboard::handle(int event) {

    int key;

    switch(event) {
        case FL_PUSH:
			if(Fl::event_button2() || Fl::event_button3()) {
				return 1;
			}

            key = getMIDIKey(Fl::event_x(), Fl::event_y());

			this->lock();

            lastMidiKey = key;
            keyStates[key] = 1;

            this->unlock();
			Fl::focus(this);
            this->redraw();

            return 1;
        case FL_DRAG:
			if(Fl::event_button2() || Fl::event_button3()) {
				return 1;
			}
            key = getMIDIKey(Fl::event_x(), Fl::event_y());

            if(key != lastMidiKey) {

            	this->lock();

                keyStates[lastMidiKey] = -1;

                if(keyStates[key] != 1) {
                	keyStates[key] = 1;
                }

                lastMidiKey = key;

                this->unlock();

                this->redraw();

            }
            return 1;
        case FL_RELEASE:
        	if(Fl::event_button1()) {
				return 1;
			}

			key = getMIDIKey(Fl::event_x(), Fl::event_y());

			this->lock();

            keyStates[key] = 0;

			if(lastMidiKey >= -1) {
            	keyStates[lastMidiKey] = -1;
			}

            lastMidiKey = -1;

            this->unlock();

            this->redraw();

            return 1;
        case FL_MOVE:
        	if(lastMidiKey >= 0) {

				this->lock();
            	keyStates[lastMidiKey] = 0;
            	lastMidiKey = -1;
            	this->unlock();

        	}
        	return 1;
    	case FL_KEYDOWN:
			//csound->Message(csound, "Key Down: Code: %d\n", Fl::event_key());
			handleKey(Fl::event_key(), 1);
			Fl::focus(this);
			this->redraw();
			return 1;
    	case FL_KEYUP:
    		if(Fl::focus() == this) {
    		//csound->Message(csound, "Key Up: Code: %d\n", Fl::event_key());
    			handleKey(Fl::event_key(), -1);
    			this->redraw();
        		return 1;
    		}
        default:
            return Fl_Widget::handle(event);
    }

}

int FLTKKeyboard::isWhiteKey(int key) {
    if(key < 3) {
        return !(key % 2);
    }

    int adjustedKey = (key - 3) % 12;

    switch(adjustedKey) {
        case 0:
        case 2:
        case 4:
        case 5:
        case 7:
        case 9:
        case 11:
            return 1;
    }
    return 0;

}

void FLTKKeyboard::draw() {
    int width = whiteKeyWidth;

    int runningX = 0;
    int yval = this->y();

    // Draw White Keys
    for(int i = 0; i < 88; i++) {
        if(isWhiteKey(i)) {
            if(keyStates[i] == 1) {
                fl_draw_box(box(), runningX, yval, width, whiteKeyHeight, FL_BLUE);
            } else {
                fl_draw_box(box(), runningX, yval, width, whiteKeyHeight, FL_WHITE);
            }
            fl_rect(runningX, yval, width, whiteKeyHeight, FL_BLACK);
            runningX += width;
        }
    }

    runningX = 0;

    // Draw Black Keys
    for(int i = 0; i < 88; i++) {
        if(isWhiteKey(i)) {
            runningX += width;
        } else {
            if(keyStates[i] == 1) {
                fl_draw_box(box(), runningX - 5, yval, blackKeyWidth, blackKeyHeight, FL_BLUE);
            } else {
                fl_draw_box(box(), runningX - 5, yval, blackKeyWidth, blackKeyHeight, FL_BLACK);
            }
        }
    }
}
<commit_msg>fixed mouse detection for black keys, draw black box for black keys<commit_after>/*
    FLTKKeyboard.cpp:

    Copyright (C) 2006 Steven Yi

    This file is part of Csound.

    The Csound Library is free software; you can redistribute it
    and/or modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    Csound 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Csound; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA
*/

#include <iostream>

#include <FL/fl_draw.H>
#include "FLTKKeyboard.hpp"

FLTKKeyboard::FLTKKeyboard(CSOUND *csound, int X, int Y, int W, int H, const char *L)
	: Fl_Widget(X, Y, W, H, L) {

    this->csound = csound;
    this->mutex = csound->Create_Mutex(0);

  	FLTKKeyboard *o = this;

	o->box(FL_FLAT_BOX);
	o->color(FL_BACKGROUND_COLOR);
	o->selection_color(FL_BACKGROUND_COLOR);
	o->labeltype(FL_NO_LABEL);
	o->labelfont(0);
	o->labelsize(14);
	o->labelcolor(FL_FOREGROUND_COLOR);
	o->user_data((void*)(this));
	o->align(FL_ALIGN_TOP);
	o->when(FL_WHEN_RELEASE);

	for(int i = 0; i < 88; i++) {
		keyStates[i] = 0;
		changedKeyStates[i] = 0;
	}

	lastMidiKey = -1;

	whiteKeys[0] = 0;
	whiteKeys[1] = 2;
	whiteKeys[2] = 4;
	whiteKeys[3] = 5;
	whiteKeys[4] = 7;
	whiteKeys[5] = 9;
	whiteKeys[6] = 11;

	octave = 5;

	aNotesOff = 0;
}

FLTKKeyboard::~FLTKKeyboard() {
	if (mutex) {
      	csound->DestroyMutex(mutex);
      	mutex = (void*) 0;
    }
}

void FLTKKeyboard::lock() {
	if(mutex) {
      	csound->LockMutex(mutex);
	}
}

void FLTKKeyboard::unlock() {
	if(mutex) {
      	csound->UnlockMutex(mutex);
	}
}

void FLTKKeyboard::allNotesOff() {
	this->lock();

	for(int i = 0; i < 88; i++) {
		this->keyStates[i] = -1;
	}

	this->lastMidiKey = -1;

	this->aNotesOff = 1;

	this->unlock();

	this->redraw();
}

int FLTKKeyboard::getMidiValForWhiteKey(int whiteKeyNum) {
    if(whiteKeyNum < 2) {
        return whiteKeyNum * 2;
    }

    int adjusted = whiteKeyNum - 2;

    int oct = adjusted / 7;
    int key = adjusted % 7;

    return 3 + (oct * 12) + whiteKeys[key];
}

int FLTKKeyboard::getMIDIKey(int x, int y) {
	if(x > this->w()) {
		return 87;
	}

	if(x < 0) {
		return 0;
	}

    // 52 white keys
    int whiteKey = x  / whiteKeyWidth;

    int extra = x % whiteKeyWidth;

    if(whiteKey < 2) {
        if(whiteKey == 0) {
            if(y > blackKeyHeight + this->y()) {
                return whiteKey;
            } else {
                if(extra > rightKeyBound) {
                    return whiteKey + 1;
                }
                return whiteKey;
            }
        } else {
            if(y > blackKeyHeight + this->y()) {
                return getMidiValForWhiteKey(whiteKey);
            } else {
                if(extra < leftKeyBound) {
                    return getMidiValForWhiteKey(whiteKey) - 1;
                }
                return getMidiValForWhiteKey(whiteKey);
            }
        }
    }

    int adjustedKey = (whiteKey - 2) % 7;

    if(adjustedKey == 0 || adjustedKey == 3) {

        if(y > blackKeyHeight + this->y()) {
            return getMidiValForWhiteKey(whiteKey);
        } else {
            if(extra > rightKeyBound) {
                return getMidiValForWhiteKey(whiteKey) + 1;
            }
            return getMidiValForWhiteKey(whiteKey);
        }
    } else if(adjustedKey == 2 || adjustedKey == 6) {
        if(y > blackKeyHeight + this->y()) {
            return getMidiValForWhiteKey(whiteKey);
        } else {
            if(extra < leftKeyBound) {
                return getMidiValForWhiteKey(whiteKey) - 1;
            }
            return getMidiValForWhiteKey(whiteKey);
        }
    }

    if(y > blackKeyHeight  + this->y()) {
        return getMidiValForWhiteKey(whiteKey);
    }

    if(extra < leftKeyBound) {
        return getMidiValForWhiteKey(whiteKey) - 1;
    }

    if(extra > rightKeyBound) {
        return getMidiValForWhiteKey(whiteKey) + 1;
    }

    return getMidiValForWhiteKey(whiteKey);

}

void FLTKKeyboard::handleKey(int key, int value) {
	int index = -1;

	switch(key) {
		case 'z':
		    index = 0;
		    break;
		case 's':
		    index = 1;
		    break;
		case 'x':
		    index = 2;
		    break;
		case 'd':
		    index = 3;
		    break;
		case 'c':
		    index = 4;
		    break;
		case 'v':
		    index = 5;
		    break;
		case 'g':
		    index = 6;
		    break;
		case 'b':
		    index = 7;
		    break;
		case 'h':
		    index = 8;
		    break;
		case 'n':
		    index = 9;
		    break;
		case 'j':
		    index = 10;
		    break;
		case 'm':
		    index = 11;
		    break;
		case 'q':
		    index = 12;
		    break;
		case '2':
		    index = 13;
		    break;
		case 'w':
		    index = 14;
		    break;
		case '3':
		    index = 15;
		    break;
		case 'e':
		    index = 16;
		    break;
		case 'r':
		    index = 17;
		    break;
		case '5':
		    index = 18;
		    break;
		case 't':
		    index = 19;
		    break;
		case '6':
		    index = 20;
		    break;
		case 'y':
		    index = 21;
		    break;
		case '7':
		    index = 22;
		    break;
		case 'u':
		    index = 23;
		    break;
		case 'i':
		    index = 24;
		    break;
		case '9':
		    index = 25;
		    break;
		case 'o':
		    index = 26;
		    break;
		case '0':
		    index = 27;
		    break;
		case 'p':
		    index = 28;
		    break;
		default:
			return;
	}

	if(index < 0) {
		return;
	}

	index = ((octave * 12) + index) - 21;

	if(index < 0 || index > 87) {
		return;
	}

	lock();
	if(keyStates[index] != value) {
		keyStates[index] = value;
	}
	unlock();
}

int FLTKKeyboard::handle(int event) {

    int key;

    switch(event) {
        case FL_PUSH:
			if(Fl::event_button2() || Fl::event_button3()) {
				return 1;
			}

            key = getMIDIKey(Fl::event_x(), Fl::event_y());

			this->lock();

            lastMidiKey = key;
            keyStates[key] = 1;

            this->unlock();
			Fl::focus(this);
            this->redraw();

            return 1;
        case FL_DRAG:
			if(Fl::event_button2() || Fl::event_button3()) {
				return 1;
			}
            key = getMIDIKey(Fl::event_x(), Fl::event_y());

            if(key != lastMidiKey) {

            	this->lock();

                keyStates[lastMidiKey] = -1;

                if(keyStates[key] != 1) {
                	keyStates[key] = 1;
                }

                lastMidiKey = key;

                this->unlock();

                this->redraw();

            }
            return 1;
        case FL_RELEASE:
        	if(Fl::event_button1()) {
				return 1;
			}

			key = getMIDIKey(Fl::event_x(), Fl::event_y());

			this->lock();

            keyStates[key] = 0;

			if(lastMidiKey >= -1) {
            	keyStates[lastMidiKey] = -1;
			}

            lastMidiKey = -1;

            this->unlock();

            this->redraw();

            return 1;
        case FL_MOVE:
        	if(lastMidiKey >= 0) {

				this->lock();
            	keyStates[lastMidiKey] = 0;
            	lastMidiKey = -1;
            	this->unlock();

        	}
        	return 1;
    	case FL_KEYDOWN:
			//csound->Message(csound, "Key Down: Code: %d\n", Fl::event_key());
			handleKey(Fl::event_key(), 1);
			Fl::focus(this);
			this->redraw();
			return 1;
    	case FL_KEYUP:
    		if(Fl::focus() == this) {
    		//csound->Message(csound, "Key Up: Code: %d\n", Fl::event_key());
    			handleKey(Fl::event_key(), -1);
    			this->redraw();
        		return 1;
    		}
        default:
            return Fl_Widget::handle(event);
    }

}

int FLTKKeyboard::isWhiteKey(int key) {
    if(key < 3) {
        return !(key % 2);
    }

    int adjustedKey = (key - 3) % 12;

    switch(adjustedKey) {
        case 0:
        case 2:
        case 4:
        case 5:
        case 7:
        case 9:
        case 11:
            return 1;
    }
    return 0;

}

void FLTKKeyboard::draw() {
    int width = whiteKeyWidth;

    int runningX = 0;
    int yval = this->y();

    // Draw White Keys
    for(int i = 0; i < 88; i++) {
        if(isWhiteKey(i)) {
            if(keyStates[i] == 1) {
                fl_draw_box(box(), runningX, yval, width, whiteKeyHeight, FL_BLUE);
            } else {
                fl_draw_box(box(), runningX, yval, width, whiteKeyHeight, FL_WHITE);
            }
            fl_rect(runningX, yval, width, whiteKeyHeight, FL_BLACK);
            runningX += width;
        }
    }

    runningX = 0;

    // Draw Black Keys
    for(int i = 0; i < 88; i++) {
        if(isWhiteKey(i)) {
            runningX += width;
        } else {
            if(keyStates[i] == 1) {
                fl_draw_box(box(), runningX - 5, yval, blackKeyWidth, blackKeyHeight, FL_BLUE);
            } else {
                fl_draw_box(box(), runningX - 5, yval, blackKeyWidth, blackKeyHeight, FL_BLACK);
            }
            fl_rect(runningX - 5, yval, blackKeyWidth, blackKeyHeight, FL_BLACK);
        }
    }
}
<|endoftext|>


<commit_before>#include <QDebug>
#include <QHBoxLayout>
#include <QLabel>
#include <QPixmap>
#include <QPushButton>
#include <QLineEdit>
#include <QRandomGenerator>
#include <QtConcurrent>

#include "common/params.h"
#include "networking.hpp"
#include "util.h"

void clearLayout(QLayout* layout) {
  while (QLayoutItem* item = layout->takeAt(0)) {
    if (QWidget* widget = item->widget()) {
      widget->deleteLater();
    }
    if (QLayout* childLayout = item->layout()) {
      clearLayout(childLayout);
    }
    delete item;
  }
}

QWidget* layoutToWidget(QLayout* l, QWidget* parent){
  QWidget* q = new QWidget(parent);
  q->setLayout(l);
  return q;
}

// https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po
std::string exec(const char* cmd) {
  std::array<char, 128> buffer;
  std::string result;
  std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
  if (!pipe) {
    throw std::runtime_error("popen() failed!");
  }
  while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
    result += buffer.data();
  }
  return result;
}

// Networking functions

Networking::Networking(QWidget* parent, bool show_advanced) : QWidget(parent), show_advanced(show_advanced){
  s = new QStackedLayout;

  QLabel* warning = new QLabel("Network manager is inactive!");
  warning->setStyleSheet(R"(font-size: 65px;)");

  s->addWidget(warning);
  setLayout(s);

  QTimer* timer = new QTimer(this);
  QObject::connect(timer, SIGNAL(timeout()), this, SLOT(refresh()));
  timer->start(5000);
  attemptInitialization();
}

void Networking::attemptInitialization(){
  // Checks if network manager is active
  try {
    wifi = new WifiManager(this);
  } catch (std::exception &e) {
    return;
  }

  connect(wifi, SIGNAL(wrongPassword(QString)), this, SLOT(wrongPassword(QString)));

  QVBoxLayout* vlayout = new QVBoxLayout;

  if (show_advanced) {
    QPushButton* advancedSettings = new QPushButton("Advanced");
    advancedSettings->setStyleSheet(R"(margin-right: 30px)");
    advancedSettings->setFixedSize(350, 100);
    connect(advancedSettings, &QPushButton::released, [=](){s->setCurrentWidget(an);});
    vlayout->addSpacing(10);
    vlayout->addWidget(advancedSettings, 0, Qt::AlignRight);
    vlayout->addSpacing(10);
  }

  wifiWidget = new WifiUI(0, 5, wifi);
  connect(wifiWidget, SIGNAL(connectToNetwork(Network)), this, SLOT(connectToNetwork(Network)));
  vlayout->addWidget(wifiWidget, 1);

  wifiScreen = layoutToWidget(vlayout, this);
  s->addWidget(wifiScreen);

  an = new AdvancedNetworking(this, wifi);
  connect(an, &AdvancedNetworking::backPress, [=](){s->setCurrentWidget(wifiScreen);});
  s->addWidget(an);

  setStyleSheet(R"(
    QPushButton {
      font-size: 50px;
      margin: 0px;
      padding: 15px;
      border-width: 0;
      border-radius: 30px;
      color: #dddddd;
      background-color: #444444;
    }
    QPushButton:disabled {
      color: #777777;
      background-color: #222222;
    }
  )");
  s->setCurrentWidget(wifiScreen);
  ui_setup_complete = true;
}

void Networking::refresh(){
  if (!this->isVisible()) {
    return;
  }
  if (!ui_setup_complete) {
    attemptInitialization();
    if (!ui_setup_complete) {
      return;
    }
  }
  wifiWidget->refresh();
  an->refresh();
}

void Networking::connectToNetwork(Network n) {
  if (n.security_type == SecurityType::OPEN) {
    wifi->connect(n);
  } else if (n.security_type == SecurityType::WPA) {
    QString pass = InputDialog::getText("Enter password for \"" + n.ssid + "\"");
    wifi->connect(n, pass);
  }
}

void Networking::wrongPassword(QString ssid) {
  return; // TODO: add this back
  /*
  for (Network n : wifi->seen_networks) {
    if (n.ssid == ssid) {
      inputField->setPromptText("Wrong password for \"" + n.ssid +"\"");
      s->setCurrentIndex(0);
      emit openKeyboard();
      return;
    }
  }
  */
}

QFrame* hline(QWidget* parent = 0){
  QFrame* line = new QFrame(parent);
  line->setFrameShape(QFrame::StyledPanel);
  line->setStyleSheet("margin-left: 40px; margin-right: 40px; border-width: 1px; border-bottom-style: solid; border-color: gray;");
  line->setFixedHeight(2);
  return line;
}

// AdvancedNetworking functions

AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi){
  s = new QStackedLayout; // mainPage, SSH settings

  QVBoxLayout* vlayout = new QVBoxLayout;

  // Back button
  QHBoxLayout* backLayout = new QHBoxLayout;
  QPushButton* back = new QPushButton("Back");
  back->setFixedSize(500, 100);
  connect(back, &QPushButton::released, [=](){emit backPress();});
  backLayout->addWidget(back, 0, Qt::AlignLeft);
  vlayout->addWidget(layoutToWidget(backLayout, this), 0, Qt::AlignLeft);

  // Enable tethering layout
  QHBoxLayout* tetheringToggleLayout = new QHBoxLayout;
  tetheringToggleLayout->addWidget(new QLabel("Enable tethering"));
  Toggle* toggle_switch = new Toggle;
  toggle_switch->setFixedSize(150, 100);
  tetheringToggleLayout->addWidget(toggle_switch);
  tetheringToggleLayout->addSpacing(40);
  if (wifi->tetheringEnabled()) {
    toggle_switch->togglePosition();
  }
  QObject::connect(toggle_switch, SIGNAL(stateChanged(int)), this, SLOT(toggleTethering(int)));
  vlayout->addWidget(layoutToWidget(tetheringToggleLayout, this), 0);
  vlayout->addWidget(hline(), 0);

  // Change tethering password
  QHBoxLayout *tetheringPassword = new QHBoxLayout;
  tetheringPassword->addWidget(new QLabel("Edit tethering password"), 1);
  editPasswordButton = new QPushButton("EDIT");
  editPasswordButton->setFixedWidth(500);
  connect(editPasswordButton, &QPushButton::released, [=](){
    QString pass = InputDialog::getText("Enter new tethering password");
    if (pass.size()) {
      wifi->changeTetheringPassword(pass);
    }
  });
  tetheringPassword->addWidget(editPasswordButton, 1, Qt::AlignRight);
  vlayout->addWidget(layoutToWidget(tetheringPassword, this), 0);
  vlayout->addWidget(hline(), 0);

  // IP adress
  QHBoxLayout* IPlayout = new QHBoxLayout;
  IPlayout->addWidget(new QLabel("IP address"), 0);
  ipLabel = new QLabel(wifi->ipv4_address);
  ipLabel->setStyleSheet("color: #aaaaaa");
  IPlayout->addWidget(ipLabel, 0, Qt::AlignRight);
  vlayout->addWidget(layoutToWidget(IPlayout, this), 0);
  vlayout->addWidget(hline(), 0);

  // Enable SSH
  QHBoxLayout* enableSSHLayout = new QHBoxLayout(this);
  enableSSHLayout->addWidget(new QLabel("Enable SSH", this));
  toggle_switch_SSH = new Toggle(this);
  toggle_switch_SSH->setFixedSize(150, 100);
  if (isSSHEnabled()) {
    toggle_switch_SSH->togglePosition();
  }
  QObject::connect(toggle_switch_SSH, SIGNAL(stateChanged(int)), this, SLOT(toggleSSH(int)));
  enableSSHLayout->addWidget(toggle_switch_SSH);
  vlayout->addWidget(layoutToWidget(enableSSHLayout, this));
  vlayout->addWidget(hline(), 0);

  // SSH keys
  QHBoxLayout* authSSHLayout = new QHBoxLayout(this);
  authSSHLayout->addWidget(new QLabel("SSH keys", this));
  QPushButton* editAuthSSHButton = new QPushButton("EDIT", this);
  editAuthSSHButton->setFixedWidth(500);
  connect(editAuthSSHButton, &QPushButton::released, [=](){s->setCurrentWidget(ssh);});
  authSSHLayout->addWidget(editAuthSSHButton);
  vlayout->addWidget(layoutToWidget(authSSHLayout, this));
  vlayout->addSpacing(50);

  // //Disconnect or delete connections
  // QHBoxLayout* dangerZone = new QHBoxLayout(this);
  // QPushButton* disconnect = new QPushButton("Disconnect from WiFi", this);
  // dangerZone->addWidget(disconnect);
  // QPushButton* deleteAll = new QPushButton("DELETE ALL NETWORKS", this);
  // dangerZone->addWidget(deleteAll);
  // vlayout->addWidget(layoutToWidget(dangerZone, this));

  // vlayout to widget
  QWidget* settingsWidget = layoutToWidget(vlayout, this);
  settingsWidget->setStyleSheet("margin-left: 40px; margin-right: 40px;");
  s->addWidget(settingsWidget);

  ssh = new SSH;
  connect(ssh, &SSH::closeSSHSettings, [=](){s->setCurrentWidget(settingsWidget);});
  s->addWidget(ssh);

  setLayout(s);
}

bool AdvancedNetworking::isSSHEnabled(){
  QString response = QString::fromStdString(exec("systemctl is-active ssh"));
  return response.startsWith("active");
}

void AdvancedNetworking::refresh(){
  ipLabel->setText(wifi->ipv4_address);
  // Don't refresh while changing SSH state
  if(!toggle_switch_SSH->getEnabled()){
    return;
  }
  if (toggle_switch_SSH->on != isSSHEnabled()) {
    toggle_switch_SSH->togglePosition();
  }
  // Qt can be lazy
  repaint();
}

void AdvancedNetworking::toggleTethering(int enable) {
  if (enable) {
    wifi->enableTethering();
  } else {
    wifi->disableTethering();
  }
  editPasswordButton->setEnabled(!enable);
}

void enableSSH(Toggle* toggle_switch_SSH){
  Params().write_db_value("SshEnabled", "1");
  toggle_switch_SSH->setEnabled(true);
}

void disableSSH(Toggle* toggle_switch_SSH){
  Params().write_db_value("SshEnabled", "0");
  toggle_switch_SSH->setEnabled(true);
}

void AdvancedNetworking::toggleSSH(int enable) {
  toggle_switch_SSH->setEnabled(false);
  if (enable) {
    QtConcurrent::run(enableSSH, toggle_switch_SSH);
  } else {
    QtConcurrent::run(disableSSH, toggle_switch_SSH);
  }
}

// WifiUI functions

WifiUI::WifiUI(QWidget *parent, int page_length, WifiManager* wifi) : QWidget(parent), networks_per_page(page_length), wifi(wifi) {
  vlayout = new QVBoxLayout;

  // Scan on startup
  QLabel *scanning = new QLabel("Scanning for networks");
  scanning->setStyleSheet(R"(font-size: 65px;)");
  vlayout->addWidget(scanning, 0, Qt::AlignCenter);
  vlayout->setSpacing(25);

  setLayout(vlayout);
  page = 0;
}

void WifiUI::refresh() {
  wifi->request_scan();
  wifi->refreshNetworks();
  clearLayout(vlayout);

  connectButtons = new QButtonGroup(this); // TODO check if this is a leak
  QObject::connect(connectButtons, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(handleButton(QAbstractButton*)));

  int i = 0;
  int countWidgets = 0;
  for (Network &network : wifi->seen_networks) {
    QHBoxLayout *hlayout = new QHBoxLayout;
    if (page * networks_per_page <= i && i < (page + 1) * networks_per_page) {
      // SSID
      hlayout->addSpacing(50);
      QString ssid = QString::fromUtf8(network.ssid);
      if(ssid.length() > 20){
        ssid = ssid.left(20 - 3) + "…";
      }

      QLabel *ssid_label = new QLabel(ssid);
      ssid_label->setStyleSheet(R"(
        font-size: 55px;
      )");
      ssid_label->setFixedWidth(this->width()*0.5);
      hlayout->addWidget(ssid_label, 0, Qt::AlignLeft);

      // TODO: don't use images for this
      // strength indicator
      unsigned int strength_scale = network.strength / 17;
      QPixmap pix("../assets/images/network_" + QString::number(strength_scale) + ".png");
      QLabel *icon = new QLabel();
      icon->setPixmap(pix.scaledToWidth(100, Qt::SmoothTransformation));
      icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
      hlayout->addWidget(icon, 0, Qt::AlignRight);

      // connect button
      QPushButton* btn = new QPushButton(network.security_type == SecurityType::UNSUPPORTED ? "Unsupported" : (network.connected == ConnectedType::CONNECTED ? "Connected" : (network.connected == ConnectedType::CONNECTING ? "Connecting" : "Connect")));
      btn->setDisabled(network.connected == ConnectedType::CONNECTED || network.connected == ConnectedType::CONNECTING || network.security_type == SecurityType::UNSUPPORTED);
      btn->setFixedWidth(350);
      hlayout->addWidget(btn, 0, Qt::AlignRight);

      connectButtons->addButton(btn, i);

      vlayout->addLayout(hlayout, 1);
      // Don't add the last horizontal line
      if (page * networks_per_page <= i+1 && i+1 < (page + 1) * networks_per_page && i+1 < wifi->seen_networks.size()) {
        vlayout->addWidget(hline(), 0);
      }
      countWidgets++;
    }
    i++;
  }
  vlayout->addStretch(3);

  // Setup buttons for pagination
  QHBoxLayout *prev_next_buttons = new QHBoxLayout;

  QPushButton* prev = new QPushButton("Previous");
  prev->setEnabled(page);
  QObject::connect(prev, SIGNAL(released()), this, SLOT(prevPage()));
  prev_next_buttons->addWidget(prev);

  QPushButton* next = new QPushButton("Next");
  next->setEnabled(wifi->seen_networks.size() > (page + 1) * networks_per_page);
  QObject::connect(next, SIGNAL(released()), this, SLOT(nextPage()));
  prev_next_buttons->addWidget(next);

  vlayout->addLayout(prev_next_buttons, 2);
}

void WifiUI::handleButton(QAbstractButton* button) {
  QPushButton* btn = static_cast<QPushButton*>(button);
  Network n = wifi->seen_networks[connectButtons->id(btn)];
  emit connectToNetwork(n);
}

void WifiUI::prevPage() {
  page--;
  refresh();
}

void WifiUI::nextPage() {
  page++;
  refresh();
}
<commit_msg>Qt-UI fix escaping all wifi access points (#20147)<commit_after>#include <QDebug>
#include <QHBoxLayout>
#include <QLabel>
#include <QPixmap>
#include <QPushButton>
#include <QLineEdit>
#include <QRandomGenerator>
#include <QtConcurrent>
#include <algorithm>

#include "common/params.h"
#include "networking.hpp"
#include "util.h"

void clearLayout(QLayout* layout) {
  while (QLayoutItem* item = layout->takeAt(0)) {
    if (QWidget* widget = item->widget()) {
      widget->deleteLater();
    }
    if (QLayout* childLayout = item->layout()) {
      clearLayout(childLayout);
    }
    delete item;
  }
}

QWidget* layoutToWidget(QLayout* l, QWidget* parent){
  QWidget* q = new QWidget(parent);
  q->setLayout(l);
  return q;
}

// https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po
std::string exec(const char* cmd) {
  std::array<char, 128> buffer;
  std::string result;
  std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
  if (!pipe) {
    throw std::runtime_error("popen() failed!");
  }
  while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
    result += buffer.data();
  }
  return result;
}

// Networking functions

Networking::Networking(QWidget* parent, bool show_advanced) : QWidget(parent), show_advanced(show_advanced){
  s = new QStackedLayout;

  QLabel* warning = new QLabel("Network manager is inactive!");
  warning->setStyleSheet(R"(font-size: 65px;)");

  s->addWidget(warning);
  setLayout(s);

  QTimer* timer = new QTimer(this);
  QObject::connect(timer, SIGNAL(timeout()), this, SLOT(refresh()));
  timer->start(5000);
  attemptInitialization();
}

void Networking::attemptInitialization(){
  // Checks if network manager is active
  try {
    wifi = new WifiManager(this);
  } catch (std::exception &e) {
    return;
  }

  connect(wifi, SIGNAL(wrongPassword(QString)), this, SLOT(wrongPassword(QString)));

  QVBoxLayout* vlayout = new QVBoxLayout;

  if (show_advanced) {
    QPushButton* advancedSettings = new QPushButton("Advanced");
    advancedSettings->setStyleSheet(R"(margin-right: 30px)");
    advancedSettings->setFixedSize(350, 100);
    connect(advancedSettings, &QPushButton::released, [=](){s->setCurrentWidget(an);});
    vlayout->addSpacing(10);
    vlayout->addWidget(advancedSettings, 0, Qt::AlignRight);
    vlayout->addSpacing(10);
  }

  wifiWidget = new WifiUI(0, 5, wifi);
  connect(wifiWidget, SIGNAL(connectToNetwork(Network)), this, SLOT(connectToNetwork(Network)));
  vlayout->addWidget(wifiWidget, 1);

  wifiScreen = layoutToWidget(vlayout, this);
  s->addWidget(wifiScreen);

  an = new AdvancedNetworking(this, wifi);
  connect(an, &AdvancedNetworking::backPress, [=](){s->setCurrentWidget(wifiScreen);});
  s->addWidget(an);

  setStyleSheet(R"(
    QPushButton {
      font-size: 50px;
      margin: 0px;
      padding: 15px;
      border-width: 0;
      border-radius: 30px;
      color: #dddddd;
      background-color: #444444;
    }
    QPushButton:disabled {
      color: #777777;
      background-color: #222222;
    }
  )");
  s->setCurrentWidget(wifiScreen);
  ui_setup_complete = true;
}

void Networking::refresh(){
  if (!this->isVisible()) {
    return;
  }
  if (!ui_setup_complete) {
    attemptInitialization();
    if (!ui_setup_complete) {
      return;
    }
  }
  wifiWidget->refresh();
  an->refresh();
}

void Networking::connectToNetwork(Network n) {
  if (n.security_type == SecurityType::OPEN) {
    wifi->connect(n);
  } else if (n.security_type == SecurityType::WPA) {
    QString pass = InputDialog::getText("Enter password for \"" + n.ssid + "\"");
    wifi->connect(n, pass);
  }
}

void Networking::wrongPassword(QString ssid) {
  return; // TODO: add this back
  /*
  for (Network n : wifi->seen_networks) {
    if (n.ssid == ssid) {
      inputField->setPromptText("Wrong password for \"" + n.ssid +"\"");
      s->setCurrentIndex(0);
      emit openKeyboard();
      return;
    }
  }
  */
}

QFrame* hline(QWidget* parent = 0){
  QFrame* line = new QFrame(parent);
  line->setFrameShape(QFrame::StyledPanel);
  line->setStyleSheet("margin-left: 40px; margin-right: 40px; border-width: 1px; border-bottom-style: solid; border-color: gray;");
  line->setFixedHeight(2);
  return line;
}

// AdvancedNetworking functions

AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi){
  s = new QStackedLayout; // mainPage, SSH settings

  QVBoxLayout* vlayout = new QVBoxLayout;

  // Back button
  QHBoxLayout* backLayout = new QHBoxLayout;
  QPushButton* back = new QPushButton("Back");
  back->setFixedSize(500, 100);
  connect(back, &QPushButton::released, [=](){emit backPress();});
  backLayout->addWidget(back, 0, Qt::AlignLeft);
  vlayout->addWidget(layoutToWidget(backLayout, this), 0, Qt::AlignLeft);

  // Enable tethering layout
  QHBoxLayout* tetheringToggleLayout = new QHBoxLayout;
  tetheringToggleLayout->addWidget(new QLabel("Enable tethering"));
  Toggle* toggle_switch = new Toggle;
  toggle_switch->setFixedSize(150, 100);
  tetheringToggleLayout->addWidget(toggle_switch);
  tetheringToggleLayout->addSpacing(40);
  if (wifi->tetheringEnabled()) {
    toggle_switch->togglePosition();
  }
  QObject::connect(toggle_switch, SIGNAL(stateChanged(int)), this, SLOT(toggleTethering(int)));
  vlayout->addWidget(layoutToWidget(tetheringToggleLayout, this), 0);
  vlayout->addWidget(hline(), 0);

  // Change tethering password
  QHBoxLayout *tetheringPassword = new QHBoxLayout;
  tetheringPassword->addWidget(new QLabel("Edit tethering password"), 1);
  editPasswordButton = new QPushButton("EDIT");
  editPasswordButton->setFixedWidth(500);
  connect(editPasswordButton, &QPushButton::released, [=](){
    QString pass = InputDialog::getText("Enter new tethering password");
    if (pass.size()) {
      wifi->changeTetheringPassword(pass);
    }
  });
  tetheringPassword->addWidget(editPasswordButton, 1, Qt::AlignRight);
  vlayout->addWidget(layoutToWidget(tetheringPassword, this), 0);
  vlayout->addWidget(hline(), 0);

  // IP adress
  QHBoxLayout* IPlayout = new QHBoxLayout;
  IPlayout->addWidget(new QLabel("IP address"), 0);
  ipLabel = new QLabel(wifi->ipv4_address);
  ipLabel->setStyleSheet("color: #aaaaaa");
  IPlayout->addWidget(ipLabel, 0, Qt::AlignRight);
  vlayout->addWidget(layoutToWidget(IPlayout, this), 0);
  vlayout->addWidget(hline(), 0);

  // Enable SSH
  QHBoxLayout* enableSSHLayout = new QHBoxLayout(this);
  enableSSHLayout->addWidget(new QLabel("Enable SSH", this));
  toggle_switch_SSH = new Toggle(this);
  toggle_switch_SSH->setFixedSize(150, 100);
  if (isSSHEnabled()) {
    toggle_switch_SSH->togglePosition();
  }
  QObject::connect(toggle_switch_SSH, SIGNAL(stateChanged(int)), this, SLOT(toggleSSH(int)));
  enableSSHLayout->addWidget(toggle_switch_SSH);
  vlayout->addWidget(layoutToWidget(enableSSHLayout, this));
  vlayout->addWidget(hline(), 0);

  // SSH keys
  QHBoxLayout* authSSHLayout = new QHBoxLayout(this);
  authSSHLayout->addWidget(new QLabel("SSH keys", this));
  QPushButton* editAuthSSHButton = new QPushButton("EDIT", this);
  editAuthSSHButton->setFixedWidth(500);
  connect(editAuthSSHButton, &QPushButton::released, [=](){s->setCurrentWidget(ssh);});
  authSSHLayout->addWidget(editAuthSSHButton);
  vlayout->addWidget(layoutToWidget(authSSHLayout, this));
  vlayout->addSpacing(50);

  // //Disconnect or delete connections
  // QHBoxLayout* dangerZone = new QHBoxLayout(this);
  // QPushButton* disconnect = new QPushButton("Disconnect from WiFi", this);
  // dangerZone->addWidget(disconnect);
  // QPushButton* deleteAll = new QPushButton("DELETE ALL NETWORKS", this);
  // dangerZone->addWidget(deleteAll);
  // vlayout->addWidget(layoutToWidget(dangerZone, this));

  // vlayout to widget
  QWidget* settingsWidget = layoutToWidget(vlayout, this);
  settingsWidget->setStyleSheet("margin-left: 40px; margin-right: 40px;");
  s->addWidget(settingsWidget);

  ssh = new SSH;
  connect(ssh, &SSH::closeSSHSettings, [=](){s->setCurrentWidget(settingsWidget);});
  s->addWidget(ssh);

  setLayout(s);
}

bool AdvancedNetworking::isSSHEnabled(){
  QString response = QString::fromStdString(exec("systemctl is-active ssh"));
  return response.startsWith("active");
}

void AdvancedNetworking::refresh(){
  ipLabel->setText(wifi->ipv4_address);
  // Don't refresh while changing SSH state
  if(!toggle_switch_SSH->getEnabled()){
    return;
  }
  if (toggle_switch_SSH->on != isSSHEnabled()) {
    toggle_switch_SSH->togglePosition();
  }
  // Qt can be lazy
  repaint();
}

void AdvancedNetworking::toggleTethering(int enable) {
  if (enable) {
    wifi->enableTethering();
  } else {
    wifi->disableTethering();
  }
  editPasswordButton->setEnabled(!enable);
}

void enableSSH(Toggle* toggle_switch_SSH){
  Params().write_db_value("SshEnabled", "1");
  toggle_switch_SSH->setEnabled(true);
}

void disableSSH(Toggle* toggle_switch_SSH){
  Params().write_db_value("SshEnabled", "0");
  toggle_switch_SSH->setEnabled(true);
}

void AdvancedNetworking::toggleSSH(int enable) {
  toggle_switch_SSH->setEnabled(false);
  if (enable) {
    QtConcurrent::run(enableSSH, toggle_switch_SSH);
  } else {
    QtConcurrent::run(disableSSH, toggle_switch_SSH);
  }
}

// WifiUI functions

WifiUI::WifiUI(QWidget *parent, int page_length, WifiManager* wifi) : QWidget(parent), networks_per_page(page_length), wifi(wifi) {
  vlayout = new QVBoxLayout;

  // Scan on startup
  QLabel *scanning = new QLabel("Scanning for networks");
  scanning->setStyleSheet(R"(font-size: 65px;)");
  vlayout->addWidget(scanning, 0, Qt::AlignCenter);
  vlayout->setSpacing(25);

  setLayout(vlayout);
  page = 0;
}

void WifiUI::refresh() {
  wifi->request_scan();
  wifi->refreshNetworks();
  clearLayout(vlayout);

  connectButtons = new QButtonGroup(this); // TODO check if this is a leak
  QObject::connect(connectButtons, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(handleButton(QAbstractButton*)));

  int i = 0;
  int countWidgets = 0;
  int pageCount = (wifi->seen_networks.size() - 1) / networks_per_page;
  page = std::max(0, std::min(page, pageCount));
  for (Network &network : wifi->seen_networks) {
    QHBoxLayout *hlayout = new QHBoxLayout;
    if (page * networks_per_page <= i && i < (page + 1) * networks_per_page) {
      // SSID
      hlayout->addSpacing(50);
      QString ssid = QString::fromUtf8(network.ssid);
      if(ssid.length() > 20){
        ssid = ssid.left(20 - 3) + "…";
      }

      QLabel *ssid_label = new QLabel(ssid);
      ssid_label->setStyleSheet(R"(
        font-size: 55px;
      )");
      ssid_label->setFixedWidth(this->width()*0.5);
      hlayout->addWidget(ssid_label, 0, Qt::AlignLeft);

      // TODO: don't use images for this
      // strength indicator
      unsigned int strength_scale = network.strength / 17;
      QPixmap pix("../assets/images/network_" + QString::number(strength_scale) + ".png");
      QLabel *icon = new QLabel();
      icon->setPixmap(pix.scaledToWidth(100, Qt::SmoothTransformation));
      icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
      hlayout->addWidget(icon, 0, Qt::AlignRight);

      // connect button
      QPushButton* btn = new QPushButton(network.security_type == SecurityType::UNSUPPORTED ? "Unsupported" : (network.connected == ConnectedType::CONNECTED ? "Connected" : (network.connected == ConnectedType::CONNECTING ? "Connecting" : "Connect")));
      btn->setDisabled(network.connected == ConnectedType::CONNECTED || network.connected == ConnectedType::CONNECTING || network.security_type == SecurityType::UNSUPPORTED);
      btn->setFixedWidth(350);
      hlayout->addWidget(btn, 0, Qt::AlignRight);

      connectButtons->addButton(btn, i);

      vlayout->addLayout(hlayout, 1);
      // Don't add the last horizontal line
      if (page * networks_per_page <= i+1 && i+1 < (page + 1) * networks_per_page && i+1 < wifi->seen_networks.size()) {
        vlayout->addWidget(hline(), 0);
      }
      countWidgets++;
    }
    i++;
  }
  vlayout->addStretch(3);

  // Setup buttons for pagination
  QHBoxLayout *prev_next_buttons = new QHBoxLayout;

  QPushButton* prev = new QPushButton("Previous");
  prev->setEnabled(page);
  QObject::connect(prev, SIGNAL(released()), this, SLOT(prevPage()));
  prev_next_buttons->addWidget(prev);

  QPushButton* next = new QPushButton("Next");
  next->setEnabled(wifi->seen_networks.size() > (page + 1) * networks_per_page);
  QObject::connect(next, SIGNAL(released()), this, SLOT(nextPage()));
  prev_next_buttons->addWidget(next);

  vlayout->addLayout(prev_next_buttons, 2);
}

void WifiUI::handleButton(QAbstractButton* button) {
  QPushButton* btn = static_cast<QPushButton*>(button);
  Network n = wifi->seen_networks[connectButtons->id(btn)];
  emit connectToNetwork(n);
}

void WifiUI::prevPage() {
  page--;
  refresh();
}

void WifiUI::nextPage() {
  page++;
  refresh();
}
<|endoftext|>


<commit_before>#include "utils.hpp"
#include "../project_info.hpp"

namespace msrv {
namespace player_foobar2000 {

Fb2kLogger::Fb2kLogger()
    : prefix_(MSRV_PROJECT_ID ": ")
{
}

Fb2kLogger::~Fb2kLogger() = default;

void Fb2kLogger::log(LogLevel, const char* fmt, va_list va)
{
    console::printfv((prefix_ + fmt).c_str(), va);
}

Fb2kWorkQueue::Fb2kWorkQueue() = default;
Fb2kWorkQueue::~Fb2kWorkQueue() = default;

void Fb2kWorkQueue::schedule(WorkCallback callback)
{
    fb2k::inMainThread(std::move(callback));
}

PlayerEventAdapter::PlayerEventAdapter()
{
    const auto flags = flag_on_playback_all & ~(flag_on_playback_dynamic_info | flag_on_playback_time);
    play_callback_manager::get()->register_callback(this, flags, false);
}

PlayerEventAdapter::~PlayerEventAdapter()
{
    play_callback_manager::get()->unregister_callback(this);
}

PlaylistEventAdapter::PlaylistEventAdapter()
{
    const auto flags = flag_on_items_added
        | flag_on_items_reordered
        | flag_on_items_removed
        | flag_on_items_modified
        | flag_on_items_replaced
        | flag_on_playlist_activate
        | flag_on_playlist_created
        | flag_on_playlists_reorder
        | flag_on_playlists_removed
        | flag_on_playlist_renamed
        | flag_on_playback_order_changed;

    playlist_manager::get()->register_callback(this, flags);
}

PlaylistEventAdapter::~PlaylistEventAdapter()
{
    playlist_manager::get()->unregister_callback(this);
}

}}
<commit_msg>foobar2000/utils.cpp: correctly subscribe to volume change events<commit_after>#include "utils.hpp"
#include "../project_info.hpp"

namespace msrv {
namespace player_foobar2000 {

Fb2kLogger::Fb2kLogger()
    : prefix_(MSRV_PROJECT_ID ": ")
{
}

Fb2kLogger::~Fb2kLogger() = default;

void Fb2kLogger::log(LogLevel, const char* fmt, va_list va)
{
    console::printfv((prefix_ + fmt).c_str(), va);
}

Fb2kWorkQueue::Fb2kWorkQueue() = default;
Fb2kWorkQueue::~Fb2kWorkQueue() = default;

void Fb2kWorkQueue::schedule(WorkCallback callback)
{
    fb2k::inMainThread(std::move(callback));
}

PlayerEventAdapter::PlayerEventAdapter()
{
    const auto flags = flag_on_playback_all
        & ~(flag_on_playback_dynamic_info | flag_on_playback_time)
        | flag_on_volume_change;

    play_callback_manager::get()->register_callback(this, flags, false);
}

PlayerEventAdapter::~PlayerEventAdapter()
{
    play_callback_manager::get()->unregister_callback(this);
}

PlaylistEventAdapter::PlaylistEventAdapter()
{
    const auto flags = flag_on_items_added
        | flag_on_items_reordered
        | flag_on_items_removed
        | flag_on_items_modified
        | flag_on_items_replaced
        | flag_on_playlist_activate
        | flag_on_playlist_created
        | flag_on_playlists_reorder
        | flag_on_playlists_removed
        | flag_on_playlist_renamed
        | flag_on_playback_order_changed;

    playlist_manager::get()->register_callback(this, flags);
}

PlaylistEventAdapter::~PlaylistEventAdapter()
{
    playlist_manager::get()->unregister_callback(this);
}

}}
<|endoftext|>


<commit_before>/**
@file Cmd/Unit.hpp
@brief Command unit.

@author Timothy Howard
@copyright 2013-2014 Timothy Howard under the MIT license;
see @ref index or the accompanying LICENSE file for full text.
*/

#ifndef HORD_CMD_UNIT_HPP_
#define HORD_CMD_UNIT_HPP_

#include <Hord/config.hpp>
#include <Hord/traits.hpp>
#include <Hord/utility.hpp>
#include <Hord/String.hpp>
#include <Hord/Cmd/Defs.hpp>
#include <Hord/System/Context.hpp>

#include <type_traits>
#include <utility>

namespace Hord {
namespace Cmd {

// Forward declarations
template<class>
class Unit;
template<class>
struct has_unit_commit_impl;
template<class, bool const>
struct unit_commit_impl;
template<class>
struct unit_ensure_traits;

/**
	@addtogroup cmd
	@{
*/

/**
	Command implementation boilerplate.

	@note This should be placed within a class definition.

	@remarks Defines base to the Cmd::Unit specialization, imports
	base::result_type, and defines all special member functions.

	@param impl_ Implementation class.
	@param name_ Name of command (string literal).
*/
#define HORD_CMD_IMPL_BOILERPLATE(impl_, name_)						\
	private:														\
		using base = ::Hord::Cmd::Unit<impl_>;						\
		using this_type = impl_;									\
		friend base;												\
	public:															\
		using base::result_type;									\
	private:														\
		impl_() = delete;											\
		impl_(impl_ const&) = delete;								\
		impl_& operator=(impl_ const&) = delete;					\
		impl_& operator=(impl_&&) = delete;							\
	public:															\
		~impl_() = default;											\
		impl_(impl_&&) = default;									\
		explicit													\
		impl_(														\
			System::Context& context								\
		) noexcept													\
			: base(context)											\
		{}															\
		static constexpr char const* const							\
		command_name = HORD_STR_LIT(name_);
//

/**
	Ensure traits for implementation class.

	@pre Must be used within the namespace @c ::Hord::Cmd.

	@param impl_ Implementation class.
*/
#define HORD_CMD_IMPL_ENSURE_TRAITS(impl_)							\
	template struct ::Hord::Cmd::unit_ensure_traits<impl_>;
//

/**
	Command implementation boilerplate with commit_impl() declaration.

	@sa
		HORD_CMD_IMPL_BOILERPLATE()
*/
#define HORD_CMD_IMPL_BOILERPLATE_WITH_COMMIT(impl_, name_)			\
	HORD_CMD_IMPL_BOILERPLATE(impl_, name_)							\
	private:														\
		void														\
		commit_impl() noexcept;
//

/**
	Define command implementation commit_impl().

	@note Function body should follow.

	@param impl_ Implementation class.
*/
#define HORD_CMD_IMPL_COMMIT_DEF(impl_)								\
	void															\
	impl_::commit_impl() noexcept

/**
	Command base class.

	@note Command implementations should derive from this class.

	@tparam Impl Command implementation class.
*/
template<class Impl>
class Unit {
public:
	/**
		Command implementation class.
	*/
	using impl_type = Impl;
	/**
		Command execution result type.
	*/
	using result_type = bool;

private:
	using this_type = Unit<impl_type>;

	using unit_commit_impl
	= Cmd::unit_commit_impl<
		impl_type,
		Cmd::has_unit_commit_impl<impl_type>::value
	>;

	System::Context& m_context;
	result_type m_result{false};
	String m_message{};

	Unit() = delete;
	Unit(Unit const&) = delete;
	Unit& operator=(Unit const&) = delete;
	Unit& operator=(Unit&&) = delete;

	void
	set_result(
		result_type const result
	) noexcept {
		m_result = result;
	}

	void
	set_message(
		String&& message
	) noexcept {
		m_message = std::move(message);
	}

public:
/** @name Special member functions */ /// @{
	/** Destructor. */
	~Unit() = default;

	/** Move constructor. */
	Unit(Unit&&) = default;

	/**
		Constructor with context.
	*/
	explicit
	Unit(
		System::Context& context
	) noexcept
		: m_context(context)
	{}
/// @}

/** @name Operators */ /// @{
	/**
		Negation operator.

		@remarks Equivalent to bad().
	*/
	bool
	operator!() const noexcept {
		return bad();
	}
/// @}

/** @name Properties */ /// @{
	/**
		Get context.
	*/
	System::Context&
	get_context() noexcept {
		return m_context;
	}

	/**
		Get context.
	*/
	System::Context const&
	get_context() const noexcept {
		return m_context;
	}

	/**
		Get driver.
	*/
	System::Driver&
	get_driver() noexcept {
		return get_context().get_driver();
	}

	/**
		Get driver.
	*/
	System::Driver const&
	get_driver() const noexcept {
		return get_context().get_driver();
	}

	/**
		Get datastore.
	*/
	IO::Datastore&
	get_datastore() noexcept {
		return get_context().get_datastore();
	}

	/**
		Get datastore.
	*/
	IO::Datastore const&
	get_datastore() const noexcept {
		return get_context().get_datastore();
	}

	/**
		Get hive.
	*/
	Hive::Unit&
	get_hive() noexcept {
		return get_context().get_hive();
	}

	/**
		Get hive.
	*/
	Hive::Unit const&
	get_hive() const noexcept {
		return get_context().get_hive();
	}

	/**
		Get error message.
	*/
	String const&
	get_message() const noexcept {
		return m_message;
	}

	/**
		Check if command execution succeeded.
	*/
	bool
	ok() const noexcept {
		return m_result;
	}

	/**
		Check if command execution failed.

		@remarks A just-constructed command unit will yield @c true.
	*/
	bool
	bad() const noexcept {
		return !m_result;
	}
/// @}

protected:
/** @name Operations */ /// @{
	/**
		Commit command with success.
	*/
	result_type
	commit() noexcept {
		set_result(true);
		unit_commit_impl::func(
			static_cast<impl_type*>(this)
		);
		return m_result;
	}

	/**
		Commit command with error message.
	*/
	result_type
	commit(
		String&& message
	) noexcept {
		set_result(false);
		set_message(std::move(message));
		unit_commit_impl::func(
			static_cast<impl_type*>(this)
		);
		return m_result;
	}
/// @}
};

/** @cond INTERNAL */
template<class Impl>
struct has_unit_commit_impl {
	template<class U>
	static auto match(void(U::*)()) -> U;

	template<class U>
	static auto test(int) -> std::is_same<U, decltype(match(&U::commit_impl))>;
	template<class>
	static auto test(...) -> std::false_type;

	static constexpr bool const
	value = decltype(test<Impl>(0))::value;
};

template<class Impl, bool const = false>
struct unit_commit_impl final {
	static void
	func(
		Impl* const
	) noexcept {
		return;
	}
};

template<class Impl>
struct unit_commit_impl<Impl, true> final {
	static void
	func(
		Impl* const impl
	) noexcept {
		return impl->commit_impl();
	}
};

template<class Impl>
struct unit_ensure_traits
	: traits::require_t<
		Impl,
		tw::capture_post<std::is_base_of, Cmd::Unit<Impl>>::template type,
		std::is_move_constructible,
		std::is_nothrow_destructible
	>
	, traits::disallow_t<
		Impl,
		tw::is_copyable,
		std::is_move_assignable
	>
{};
/** @endcond */ // INTERNAL

/** @} */ // end of doc-group cmd

} // namespace Cmd
} // namespace Hord

#endif // HORD_CMD_UNIT_HPP_
<commit_msg>Cmd::Unit: changed command_name accessor to a func.¹<commit_after>/**
@file Cmd/Unit.hpp
@brief Command unit.

@author Timothy Howard
@copyright 2013-2014 Timothy Howard under the MIT license;
see @ref index or the accompanying LICENSE file for full text.
*/

#ifndef HORD_CMD_UNIT_HPP_
#define HORD_CMD_UNIT_HPP_

#include <Hord/config.hpp>
#include <Hord/traits.hpp>
#include <Hord/utility.hpp>
#include <Hord/String.hpp>
#include <Hord/Cmd/Defs.hpp>
#include <Hord/System/Context.hpp>

#include <type_traits>
#include <utility>

namespace Hord {
namespace Cmd {

// Forward declarations
template<class>
class Unit;
template<class>
struct has_unit_commit_impl;
template<class, bool const>
struct unit_commit_impl;
template<class>
struct unit_ensure_traits;

/**
	@addtogroup cmd
	@{
*/

/**
	Command implementation boilerplate.

	@note This should be placed within a class definition.

	@remarks Defines base to the Cmd::Unit specialization, imports
	base::result_type, and defines all special member functions.

	@param impl_ Implementation class.
	@param name_ Name of command (string literal).
*/
#define HORD_CMD_IMPL_BOILERPLATE(impl_, name_)						\
	private:														\
		using base = ::Hord::Cmd::Unit<impl_>;						\
		using this_type = impl_;									\
		friend base;												\
	public:															\
		using base::result_type;									\
	private:														\
		impl_() = delete;											\
		impl_(impl_ const&) = delete;								\
		impl_& operator=(impl_ const&) = delete;					\
		impl_& operator=(impl_&&) = delete;							\
	public:															\
		~impl_() = default;											\
		impl_(impl_&&) = default;									\
		explicit													\
		impl_(														\
			System::Context& context								\
		) noexcept													\
			: base(context)											\
		{}															\
		static constexpr char const*								\
		command_name() noexcept { return HORD_STR_LIT(name_); }
//

/**
	Ensure traits for implementation class.

	@pre Must be used within the namespace @c ::Hord::Cmd.

	@param impl_ Implementation class.
*/
#define HORD_CMD_IMPL_ENSURE_TRAITS(impl_)							\
	template struct ::Hord::Cmd::unit_ensure_traits<impl_>;
//

/**
	Command implementation boilerplate with commit_impl() declaration.

	@sa
		HORD_CMD_IMPL_BOILERPLATE()
*/
#define HORD_CMD_IMPL_BOILERPLATE_WITH_COMMIT(impl_, name_)			\
	HORD_CMD_IMPL_BOILERPLATE(impl_, name_)							\
	private:														\
		void														\
		commit_impl() noexcept;
//

/**
	Define command implementation commit_impl().

	@note Function body should follow.

	@param impl_ Implementation class.
*/
#define HORD_CMD_IMPL_COMMIT_DEF(impl_)								\
	void															\
	impl_::commit_impl() noexcept

/**
	Command base class.

	@note Command implementations should derive from this class.

	@tparam Impl Command implementation class.
*/
template<class Impl>
class Unit {
public:
	/**
		Command implementation class.
	*/
	using impl_type = Impl;
	/**
		Command execution result type.
	*/
	using result_type = bool;

private:
	using this_type = Unit<impl_type>;

	using unit_commit_impl
	= Cmd::unit_commit_impl<
		impl_type,
		Cmd::has_unit_commit_impl<impl_type>::value
	>;

	System::Context& m_context;
	result_type m_result{false};
	String m_message{};

	Unit() = delete;
	Unit(Unit const&) = delete;
	Unit& operator=(Unit const&) = delete;
	Unit& operator=(Unit&&) = delete;

	void
	set_result(
		result_type const result
	) noexcept {
		m_result = result;
	}

	void
	set_message(
		String&& message
	) noexcept {
		m_message = std::move(message);
	}

public:
/** @name Special member functions */ /// @{
	/** Destructor. */
	~Unit() = default;

	/** Move constructor. */
	Unit(Unit&&) = default;

	/**
		Constructor with context.
	*/
	explicit
	Unit(
		System::Context& context
	) noexcept
		: m_context(context)
	{}
/// @}

/** @name Operators */ /// @{
	/**
		Negation operator.

		@remarks Equivalent to bad().
	*/
	bool
	operator!() const noexcept {
		return bad();
	}
/// @}

/** @name Properties */ /// @{
	/**
		Get context.
	*/
	System::Context&
	get_context() noexcept {
		return m_context;
	}

	/**
		Get context.
	*/
	System::Context const&
	get_context() const noexcept {
		return m_context;
	}

	/**
		Get driver.
	*/
	System::Driver&
	get_driver() noexcept {
		return get_context().get_driver();
	}

	/**
		Get driver.
	*/
	System::Driver const&
	get_driver() const noexcept {
		return get_context().get_driver();
	}

	/**
		Get datastore.
	*/
	IO::Datastore&
	get_datastore() noexcept {
		return get_context().get_datastore();
	}

	/**
		Get datastore.
	*/
	IO::Datastore const&
	get_datastore() const noexcept {
		return get_context().get_datastore();
	}

	/**
		Get hive.
	*/
	Hive::Unit&
	get_hive() noexcept {
		return get_context().get_hive();
	}

	/**
		Get hive.
	*/
	Hive::Unit const&
	get_hive() const noexcept {
		return get_context().get_hive();
	}

	/**
		Get error message.
	*/
	String const&
	get_message() const noexcept {
		return m_message;
	}

	/**
		Check if command execution succeeded.
	*/
	bool
	ok() const noexcept {
		return m_result;
	}

	/**
		Check if command execution failed.

		@remarks A just-constructed command unit will yield @c true.
	*/
	bool
	bad() const noexcept {
		return !m_result;
	}
/// @}

protected:
/** @name Operations */ /// @{
	/**
		Commit command with success.
	*/
	result_type
	commit() noexcept {
		set_result(true);
		unit_commit_impl::func(
			static_cast<impl_type*>(this)
		);
		return m_result;
	}

	/**
		Commit command with error message.
	*/
	result_type
	commit(
		String&& message
	) noexcept {
		set_result(false);
		set_message(std::move(message));
		unit_commit_impl::func(
			static_cast<impl_type*>(this)
		);
		return m_result;
	}
/// @}
};

/** @cond INTERNAL */
template<class Impl>
struct has_unit_commit_impl {
	template<class U>
	static auto match(void(U::*)()) -> U;

	template<class U>
	static auto test(int) -> std::is_same<U, decltype(match(&U::commit_impl))>;
	template<class>
	static auto test(...) -> std::false_type;

	static constexpr bool const
	value = decltype(test<Impl>(0))::value;
};

template<class Impl, bool const = false>
struct unit_commit_impl final {
	static void
	func(
		Impl* const
	) noexcept {
		return;
	}
};

template<class Impl>
struct unit_commit_impl<Impl, true> final {
	static void
	func(
		Impl* const impl
	) noexcept {
		return impl->commit_impl();
	}
};

template<class Impl>
struct unit_ensure_traits
	: traits::require_t<
		Impl,
		tw::capture_post<std::is_base_of, Cmd::Unit<Impl>>::template type,
		std::is_move_constructible,
		std::is_nothrow_destructible
	>
	, traits::disallow_t<
		Impl,
		tw::is_copyable,
		std::is_move_assignable
	>
{};
/** @endcond */ // INTERNAL

/** @} */ // end of doc-group cmd

} // namespace Cmd
} // namespace Hord

#endif // HORD_CMD_UNIT_HPP_
<|endoftext|>


<commit_before>#include <numa.h>
#include <sys/mman.h>

#include "slab_allocator.h"

int slab_allocator::alloc(char **objs, int nobjs) {
#ifdef MEMCHECK
	for (int i = 0; i < nobjs; i++)
		objs[i] = (char *) allocator.alloc(obj_size);
	return nobjs;
#else
	int num = 0;

	while (true) {
		pthread_spin_lock(&lock);
		linked_obj *o = list.pop(nobjs - num);
		pthread_spin_unlock(&lock);
		while (o != NULL) {
			objs[num++] = (char *) o;
			o = o->get_next();
		}
		if (num == nobjs)
			break;

		// This piece of code shouldn't be executed very frequently,
		// otherwise, the performance can be pretty bad.
		pthread_spin_lock(&lock);
		if (curr_size < max_size) {
			// We should increase the current size in advance, so other threads
			// can see what this thread is doing here.
			curr_size += increase_size;
			pthread_spin_unlock(&lock);
			char *objs;
			if (node_id == -1)
				objs = (char *) numa_alloc_local(increase_size);
			else
				objs = (char *) numa_alloc_onnode(increase_size, node_id);
#ifdef USE_IOAT
			if (pinned) {
				int ret = mlock(objs, increase_size);
				if (ret < 0)
					perror("mlock");
				assert(ret == 0);
			}
#endif
			assert(((long) objs) % PAGE_SIZE == 0);
			if (init)
				memset(objs, 0, increase_size);
			linked_obj_list tmp_list;
			for (int i = 0; i < increase_size / obj_size; i++) {
				linked_obj *header = (linked_obj *) (objs
						+ obj_size * i);
				*header = linked_obj();
				tmp_list.add(header);
			}
			pthread_spin_lock(&lock);
			alloc_bufs.push_back(objs);
			list.add_list(&tmp_list);
			pthread_spin_unlock(&lock);
		}
		else {
			pthread_spin_unlock(&lock);
			// If we can't allocate all objects, then free all objects that
			// have been allocated, and return 0.
			free(objs, num);
			return 0;
		}
	}
	return nobjs;
#endif
}

slab_allocator::~slab_allocator()
{
	for (unsigned i = 0; i < alloc_bufs.size(); i++) {
#ifdef USE_IOAT
		if (pinned) {
#ifdef DEBUG
			printf("unpin buf %p of %ld bytes\n", alloc_bufs[i], increase_size);
#endif
			munlock(alloc_bufs[i], increase_size);
		}
#endif
		numa_free(alloc_bufs[i], increase_size);
	}
	pthread_spin_destroy(&lock);
}

void slab_allocator::free(char **objs, int nobjs) {
#ifdef MEMCHECK
	for (int i = 0; i < nobjs; i++)
		allocator.dealloc(objs[i]);
#else
	linked_obj_list tmp_list;
	for (int i = 0; i < nobjs; i++) {
		linked_obj *o = (linked_obj *) objs[i];
		*o = linked_obj();
		tmp_list.add(o);
	}
	pthread_spin_lock(&lock);
	list.add_list(&tmp_list);
	pthread_spin_unlock(&lock);
#endif
}

<commit_msg>fix a bug in slab_allocator when MEMCHECK is enabled.<commit_after>#include <numa.h>
#include <sys/mman.h>

#include "slab_allocator.h"

int slab_allocator::alloc(char **objs, int nobjs) {
#ifdef MEMCHECK
	for (int i = 0; i < nobjs; i++) {
		objs[i] = (char *) allocator.alloc(obj_size);
		if (init)
			memset(objs[i], 0, obj_size);
	}
	return nobjs;
#else
	int num = 0;

	while (true) {
		pthread_spin_lock(&lock);
		linked_obj *o = list.pop(nobjs - num);
		pthread_spin_unlock(&lock);
		while (o != NULL) {
			objs[num++] = (char *) o;
			o = o->get_next();
		}
		if (num == nobjs)
			break;

		// This piece of code shouldn't be executed very frequently,
		// otherwise, the performance can be pretty bad.
		pthread_spin_lock(&lock);
		if (curr_size < max_size) {
			// We should increase the current size in advance, so other threads
			// can see what this thread is doing here.
			curr_size += increase_size;
			pthread_spin_unlock(&lock);
			char *objs;
			if (node_id == -1)
				objs = (char *) numa_alloc_local(increase_size);
			else
				objs = (char *) numa_alloc_onnode(increase_size, node_id);
#ifdef USE_IOAT
			if (pinned) {
				int ret = mlock(objs, increase_size);
				if (ret < 0)
					perror("mlock");
				assert(ret == 0);
			}
#endif
			assert(((long) objs) % PAGE_SIZE == 0);
			if (init)
				memset(objs, 0, increase_size);
			linked_obj_list tmp_list;
			for (int i = 0; i < increase_size / obj_size; i++) {
				linked_obj *header = (linked_obj *) (objs
						+ obj_size * i);
				*header = linked_obj();
				tmp_list.add(header);
			}
			pthread_spin_lock(&lock);
			alloc_bufs.push_back(objs);
			list.add_list(&tmp_list);
			pthread_spin_unlock(&lock);
		}
		else {
			pthread_spin_unlock(&lock);
			// If we can't allocate all objects, then free all objects that
			// have been allocated, and return 0.
			free(objs, num);
			return 0;
		}
	}
	return nobjs;
#endif
}

slab_allocator::~slab_allocator()
{
	for (unsigned i = 0; i < alloc_bufs.size(); i++) {
#ifdef USE_IOAT
		if (pinned) {
#ifdef DEBUG
			printf("unpin buf %p of %ld bytes\n", alloc_bufs[i], increase_size);
#endif
			munlock(alloc_bufs[i], increase_size);
		}
#endif
		numa_free(alloc_bufs[i], increase_size);
	}
	pthread_spin_destroy(&lock);
}

void slab_allocator::free(char **objs, int nobjs) {
#ifdef MEMCHECK
	for (int i = 0; i < nobjs; i++)
		allocator.dealloc(objs[i]);
#else
	linked_obj_list tmp_list;
	for (int i = 0; i < nobjs; i++) {
		linked_obj *o = (linked_obj *) objs[i];
		*o = linked_obj();
		tmp_list.add(o);
	}
	pthread_spin_lock(&lock);
	list.add_list(&tmp_list);
	pthread_spin_unlock(&lock);
#endif
}

<|endoftext|>


<commit_before>#include "page.h"
#include "bloom_filter.h"
#include <fstream>
#include <cassert>
#include <algorithm>
#include <cstring>

using namespace dariadb::storage;

class PageCursor : public dariadb::storage::Cursor {
public:
	PageCursor(Page*page, const dariadb::IdArray&ids, dariadb::Time from, dariadb::Time to, dariadb::Flag flag) :
		link(page),
		_ids(ids),
		_from(from),
		_to(to),
		_flag(flag)
	{
		reset_pos();
	}

	~PageCursor() {
		if (link != nullptr) {
			link->dec_reader();
			link = nullptr;
		}
	}

	bool is_end()const override {
		return _is_end;
	}

	void readNext(Cursor::Callback*cbk)  override {
		//TODO refact this
		std::lock_guard<std::mutex> lg(_locker);
        if(read_poses.empty()){
			_is_end = true;
            return;
        }
        auto _index_it=this->link->index[read_poses.front()];
        read_poses.pop_front();
        for (; !_is_end; ) {
            if (_is_end) {
				Chunk_Ptr empty;
				cbk->call(empty);
				_is_end = true;
				break;
			}

            if ((_ids.size() != 0) && (std::find(_ids.begin(), _ids.end(), _index_it.info.first.id) == _ids.end())) {
				if (!read_poses.empty()) {
					_index_it = this->link->index[read_poses.front()];
					read_poses.pop_front();
				}
				else { break; }
				continue;
			}

            if (!dariadb::storage::bloom_check(_index_it.info.flag_bloom, _flag)) {
				if (!read_poses.empty()) {
					_index_it = this->link->index[read_poses.front()];
					read_poses.pop_front();
				}
				else { break; }
				continue;
			}

			if (check_index_rec(_index_it)) {
                auto ptr = new Chunk(_index_it.info, link->chunks + _index_it.offset, link->header->chunk_size);
				Chunk_Ptr c{ ptr };
				assert(c->last.time != 0);
				cbk->call(c);
				break;
			}
			else {//end of data;
				_is_end = true;
				Chunk_Ptr empty;
				cbk->call(empty);
				break;
			}
		}
		if (read_poses.empty()) {
			_is_end = true;
			return;
		}
	}

    bool check_index_rec(Page_ChunkIndex&it) const{
        return ((dariadb::utils::inInterval(_from, _to, it.info.minTime)) || (dariadb::utils::inInterval(_from, _to, it.info.maxTime)));
	}

	void reset_pos() override { //start read from begining;
		_is_end = false;
        this->read_poses.clear();
		//TODO lock this->link; does'n need when call from ctor.
		auto sz = this->link->_itree.size();
		if (sz != 0) {
			auto it_to = this->link->_itree.end();// this->link->_itree.upper_bound(this->_to);
			auto it_from = this->link->_itree.begin();//this->link->_itree.lower_bound(this->_from);

            /*if(it_from!= this->link->_itree.begin()){
                if(it_from->first!=this->_from){
                    --it_from;
                }
            }*/
            for(auto it=it_from;it!=this->link->_itree.end();++it){
                this->read_poses.push_back(it->second);
                if(it==it_to){
                    break;
                }
            }
            if(read_poses.empty()){
                _is_end=true;
                return;
            }
        }else{
            _is_end=true;
            return;
        }
	}
protected:
	Page* link;
	bool _is_end;
	dariadb::IdArray _ids;
	dariadb::Time _from, _to;
	dariadb::Flag _flag;
	std::mutex _locker;
    std::list<uint32_t> read_poses;
};

Page::~Page() {
	region=nullptr;
	header=nullptr;
	index=nullptr;
	chunks=nullptr;
	mmap->close();
}

Page* Page::create(std::string file_name, uint64_t sz, uint32_t chunk_per_storage, uint32_t chunk_size, MODE mode) {
	auto res = new Page;
	auto mmap = utils::fs::MappedFile::touch(file_name, sz);
	auto region = mmap->data();
	std::fill(region, region + sz, 0);

	res->mmap = mmap;
	res->region = region;
	res->header = reinterpret_cast<PageHeader*>(region);
	res->index = reinterpret_cast<Page_ChunkIndex*>(region + sizeof(PageHeader));
	res->chunks = reinterpret_cast<uint8_t*>(region + sizeof(PageHeader) + sizeof(Page_ChunkIndex)*chunk_per_storage);

	res->header->chunk_per_storage = chunk_per_storage;
	res->header->chunk_size = chunk_size;
	res->header->maxTime = dariadb::Time(0);
	res->header->minTime = std::numeric_limits<dariadb::Time>::max();
    res->header->is_overwrite=false;
    res->header->mode=mode;

	for (size_t i = 0; i < res->header->chunk_per_storage; ++i) {
		auto irec = &res->index[i];
		assert(!irec->is_init);
		res->_free_poses.push_back(i);
	}
	return res;
}

size_t get_header_offset(){
    return 0;
}

size_t get_index_offset(){
    return sizeof(PageHeader);
}

size_t get_chunks_offset(uint32_t chunk_per_storage){
    return sizeof(PageHeader) + sizeof(Page_ChunkIndex)*chunk_per_storage;
}

Page* Page::open(std::string file_name) {
	auto res = new Page;
	auto mmap = utils::fs::MappedFile::open(file_name);

	auto region = mmap->data();

	res->mmap = mmap;
	res->region = region;
    res->header = reinterpret_cast<PageHeader*>(region)+get_header_offset();
    res->index = reinterpret_cast<Page_ChunkIndex*>(region + get_index_offset());
    res->chunks = reinterpret_cast<uint8_t*>(region + get_chunks_offset(res->header->chunk_per_storage));
	if (res->header->chunk_size == 0) {
		throw MAKE_EXCEPTION("(res->header->chunk_size == 0)");
	}

	for (size_t i = 0; i < res->header->chunk_per_storage; ++i) {
		auto irec = &res->index[i];
		if (!irec->is_init) {
			res->_free_poses.push_back(i);
		}
		else {
			res->_itree.insert(std::make_pair(irec->info.maxTime, i));
		}
	}
	return res;
}

PageHeader Page::readHeader(std::string file_name) {
	std::ifstream istream;
	istream.open(file_name, std::fstream::in | std::fstream::binary);
	if (!istream.is_open()) {
		std::stringstream ss;
		ss << "can't open file. filename=" << file_name;
		throw MAKE_EXCEPTION(ss.str());
	}
	PageHeader result;
	memset(&result, 0, sizeof(PageHeader));
	istream.read((char *)&result, sizeof(PageHeader));
	istream.close();
	return result;

}

uint32_t Page::get_oldes_index() {
    auto min_time = std::numeric_limits<dariadb::Time>::max();
    uint32_t pos = 0;
    auto index_end = index + header->chunk_per_storage;
    uint32_t i = 0;
    for (auto index_it = index; index_it != index_end; ++index_it, ++i) {
        if (index_it->info.minTime<min_time) {
            pos = i;
            min_time = index_it->info.minTime;
        }
    }
    return pos;
}

bool Page::append(const ChunksList&ch){
    for(auto &c:ch){
        if(!this->append(c)){
            return false;
        }
    }
    return true;
}
bool Page::append(const Chunk_Ptr&ch) {
    std::lock_guard<std::mutex> lg(_locker);
	auto index_rec = (ChunkIndexInfo*)ch.get();
	auto buffer = ch->_buffer_t.data();

	assert(ch->last.time != 0);
	assert(header->chunk_size == ch->_buffer_t.size());
	uint32_t pos_index=0;
	if (is_full()) {
        if (header->mode == MODE::SINGLE) {
            header->is_overwrite=true;
			pos_index = this->_itree.begin()->second;
        }
		else {
			return false;
		}
	}
	else {
		pos_index = _free_poses.front();
		_free_poses.pop_front();
	}
	index[pos_index].info = *index_rec;
    index[pos_index].is_init = true;

    if(!header->is_overwrite){
        index[pos_index].offset = header->pos_chunks;
        header->pos_chunks += header->chunk_size;
        header->addeded_chunks++;
		_itree.insert(std::make_pair(index_rec->maxTime, pos_index));
	}
	else {
		auto it = this->_itree.begin();
		auto min_time = it->first;
		_itree.erase(it);
		_itree.insert(std::make_pair(index_rec->maxTime, pos_index));
	}
	memcpy(this->chunks + index[pos_index].offset, buffer, sizeof(uint8_t)*header->chunk_size);

	header->minTime = std::min(header->minTime,ch->minTime);
	header->maxTime = std::max(header->maxTime, ch->maxTime);

    this->mmap->flush(get_header_offset(),sizeof(PageHeader));
    this->mmap->flush(get_index_offset()+sizeof(Page_ChunkIndex),sizeof(Page_ChunkIndex));
    auto offset=get_chunks_offset(header->chunk_per_storage)+size_t(this->chunks - index[pos_index].offset);
    this->mmap->flush(offset,sizeof(header->chunk_size));
    return true;
}

bool Page::is_full()const {
	return this->_free_poses.empty();
}

Cursor_ptr Page::get_chunks(const dariadb::IdArray&ids, dariadb::Time from, dariadb::Time to, dariadb::Flag flag) {
    std::lock_guard<std::mutex> lg(_locker);

	auto raw_ptr = new PageCursor(this,ids,from,to,flag );
	Cursor_ptr result{ raw_ptr };

	header->count_readers++;

	return result;
}

ChunksList Page::get_open_chunks() {
    std::lock_guard<std::mutex> lg(_locker);
	auto index_end = this->index + this->header->chunk_per_storage;
	auto index_it = this->index;
	ChunksList result;
	for (uint32_t pos = 0; index_it != index_end; ++index_it, ++pos) {
		if (!index_it->is_init) {
			continue;
		}
		if (!index_it->info.is_readonly) {
			index_it->is_init = false;
            auto ptr=new Chunk(index_it->info, this->chunks + index_it->offset, this->header->chunk_size);
            Chunk_Ptr c = Chunk_Ptr(ptr);
			result.push_back(c);
			index_it->is_init = false;
			this->header->addeded_chunks--;
			_free_poses.push_back(pos);
		}
	}
	return result;
}

void Page::dec_reader(){
    std::lock_guard<std::mutex> lg(_locker);
	header->count_readers--;
}

Cursor_ptr dariadb::storage::Page::chunksByIterval(const IdArray & ids, Flag flag, Time from, Time to)
{
	return get_chunks(ids, from, to, flag);
}

IdToChunkMap dariadb::storage::Page::chunksBeforeTimePoint(const IdArray & ids, Flag flag, Time timePoint)
{
	IdToChunkMap result;

	ChunksList ch_list;
	auto cursor = this->get_chunks(ids, header->minTime, timePoint, flag);
	if (cursor == nullptr) {
		return result;
	}
	cursor->readAll(&ch_list);

	for (auto&v : ch_list) {
		auto find_res = result.find(v->first.id);
		if (find_res == result.end()) {
			result.insert(std::make_pair(v->first.id, v));
		}
		else {
			if (find_res->second->maxTime < v->maxTime) {
				result[v->first.id] = v;
			}
		}
	}
	return result;
}
class CountOfIdCallback :public Cursor::Callback {
public:
	dariadb::IdSet ids;
	CountOfIdCallback() {
	}
	~CountOfIdCallback() {
	}

	virtual void call(Chunk_Ptr & ptr) override {
		if (ptr != nullptr) {
			ids.insert(ptr->first.id);
		}
	}
};

dariadb::IdArray dariadb::storage::Page::getIds()
{
	auto cursor = get_chunks(dariadb::IdArray{}, header->minTime, header->maxTime, 0);

	CountOfIdCallback*clbk_raw = new CountOfIdCallback;
	std::unique_ptr<Cursor::Callback> clbk{ clbk_raw };

	cursor->readAll(clbk.get());

	return dariadb::IdArray{ clbk_raw->ids.begin(),clbk_raw->ids.end() };
}
<commit_msg>unused variable.<commit_after>#include "page.h"
#include "bloom_filter.h"
#include <fstream>
#include <cassert>
#include <algorithm>
#include <cstring>

using namespace dariadb::storage;

class PageCursor : public dariadb::storage::Cursor {
public:
	PageCursor(Page*page, const dariadb::IdArray&ids, dariadb::Time from, dariadb::Time to, dariadb::Flag flag) :
		link(page),
		_ids(ids),
		_from(from),
		_to(to),
		_flag(flag)
	{
		reset_pos();
	}

	~PageCursor() {
		if (link != nullptr) {
			link->dec_reader();
			link = nullptr;
		}
	}

	bool is_end()const override {
		return _is_end;
	}

	void readNext(Cursor::Callback*cbk)  override {
		//TODO refact this
		std::lock_guard<std::mutex> lg(_locker);
        if(read_poses.empty()){
			_is_end = true;
            return;
        }
        auto _index_it=this->link->index[read_poses.front()];
        read_poses.pop_front();
        for (; !_is_end; ) {
            if (_is_end) {
				Chunk_Ptr empty;
				cbk->call(empty);
				_is_end = true;
				break;
			}

            if ((_ids.size() != 0) && (std::find(_ids.begin(), _ids.end(), _index_it.info.first.id) == _ids.end())) {
				if (!read_poses.empty()) {
					_index_it = this->link->index[read_poses.front()];
					read_poses.pop_front();
				}
				else { break; }
				continue;
			}

            if (!dariadb::storage::bloom_check(_index_it.info.flag_bloom, _flag)) {
				if (!read_poses.empty()) {
					_index_it = this->link->index[read_poses.front()];
					read_poses.pop_front();
				}
				else { break; }
				continue;
			}

			if (check_index_rec(_index_it)) {
                auto ptr = new Chunk(_index_it.info, link->chunks + _index_it.offset, link->header->chunk_size);
				Chunk_Ptr c{ ptr };
				assert(c->last.time != 0);
				cbk->call(c);
				break;
			}
			else {//end of data;
				_is_end = true;
				Chunk_Ptr empty;
				cbk->call(empty);
				break;
			}
		}
		if (read_poses.empty()) {
			_is_end = true;
			return;
		}
	}

    bool check_index_rec(Page_ChunkIndex&it) const{
        return ((dariadb::utils::inInterval(_from, _to, it.info.minTime)) || (dariadb::utils::inInterval(_from, _to, it.info.maxTime)));
	}

	void reset_pos() override { //start read from begining;
		_is_end = false;
        this->read_poses.clear();
		//TODO lock this->link; does'n need when call from ctor.
		auto sz = this->link->_itree.size();
		if (sz != 0) {
			auto it_to = this->link->_itree.end();// this->link->_itree.upper_bound(this->_to);
			auto it_from = this->link->_itree.begin();//this->link->_itree.lower_bound(this->_from);

            /*if(it_from!= this->link->_itree.begin()){
                if(it_from->first!=this->_from){
                    --it_from;
                }
            }*/
            for(auto it=it_from;it!=this->link->_itree.end();++it){
                this->read_poses.push_back(it->second);
                if(it==it_to){
                    break;
                }
            }
            if(read_poses.empty()){
                _is_end=true;
                return;
            }
        }else{
            _is_end=true;
            return;
        }
	}
protected:
	Page* link;
	bool _is_end;
	dariadb::IdArray _ids;
	dariadb::Time _from, _to;
	dariadb::Flag _flag;
	std::mutex _locker;
    std::list<uint32_t> read_poses;
};

Page::~Page() {
	region=nullptr;
	header=nullptr;
	index=nullptr;
	chunks=nullptr;
	mmap->close();
}

Page* Page::create(std::string file_name, uint64_t sz, uint32_t chunk_per_storage, uint32_t chunk_size, MODE mode) {
	auto res = new Page;
	auto mmap = utils::fs::MappedFile::touch(file_name, sz);
	auto region = mmap->data();
	std::fill(region, region + sz, 0);

	res->mmap = mmap;
	res->region = region;
	res->header = reinterpret_cast<PageHeader*>(region);
	res->index = reinterpret_cast<Page_ChunkIndex*>(region + sizeof(PageHeader));
	res->chunks = reinterpret_cast<uint8_t*>(region + sizeof(PageHeader) + sizeof(Page_ChunkIndex)*chunk_per_storage);

	res->header->chunk_per_storage = chunk_per_storage;
	res->header->chunk_size = chunk_size;
	res->header->maxTime = dariadb::Time(0);
	res->header->minTime = std::numeric_limits<dariadb::Time>::max();
    res->header->is_overwrite=false;
    res->header->mode=mode;

	for (uint32_t i = 0; i < res->header->chunk_per_storage; ++i) {
		auto irec = &res->index[i];
		assert(!irec->is_init);
		res->_free_poses.push_back(i);
	}
	return res;
}

size_t get_header_offset(){
    return 0;
}

size_t get_index_offset(){
    return sizeof(PageHeader);
}

size_t get_chunks_offset(uint32_t chunk_per_storage){
    return sizeof(PageHeader) + sizeof(Page_ChunkIndex)*chunk_per_storage;
}

Page* Page::open(std::string file_name) {
	auto res = new Page;
	auto mmap = utils::fs::MappedFile::open(file_name);

	auto region = mmap->data();

	res->mmap = mmap;
	res->region = region;
    res->header = reinterpret_cast<PageHeader*>(region)+get_header_offset();
    res->index = reinterpret_cast<Page_ChunkIndex*>(region + get_index_offset());
    res->chunks = reinterpret_cast<uint8_t*>(region + get_chunks_offset(res->header->chunk_per_storage));
	if (res->header->chunk_size == 0) {
		throw MAKE_EXCEPTION("(res->header->chunk_size == 0)");
	}

	for (uint32_t i = 0; i < res->header->chunk_per_storage; ++i) {
		auto irec = &res->index[i];
		if (!irec->is_init) {
			res->_free_poses.push_back(i);
		}
		else {
			res->_itree.insert(std::make_pair(irec->info.maxTime, i));
		}
	}
	return res;
}

PageHeader Page::readHeader(std::string file_name) {
	std::ifstream istream;
	istream.open(file_name, std::fstream::in | std::fstream::binary);
	if (!istream.is_open()) {
		std::stringstream ss;
		ss << "can't open file. filename=" << file_name;
		throw MAKE_EXCEPTION(ss.str());
	}
	PageHeader result;
	memset(&result, 0, sizeof(PageHeader));
	istream.read((char *)&result, sizeof(PageHeader));
	istream.close();
	return result;

}

uint32_t Page::get_oldes_index() {
    auto min_time = std::numeric_limits<dariadb::Time>::max();
    uint32_t pos = 0;
    auto index_end = index + header->chunk_per_storage;
    uint32_t i = 0;
    for (auto index_it = index; index_it != index_end; ++index_it, ++i) {
        if (index_it->info.minTime<min_time) {
            pos = i;
            min_time = index_it->info.minTime;
        }
    }
    return pos;
}

bool Page::append(const ChunksList&ch){
    for(auto &c:ch){
        if(!this->append(c)){
            return false;
        }
    }
    return true;
}
bool Page::append(const Chunk_Ptr&ch) {
    std::lock_guard<std::mutex> lg(_locker);
	auto index_rec = (ChunkIndexInfo*)ch.get();
	auto buffer = ch->_buffer_t.data();

	assert(ch->last.time != 0);
	assert(header->chunk_size == ch->_buffer_t.size());
	uint32_t pos_index=0;
	if (is_full()) {
        if (header->mode == MODE::SINGLE) {
            header->is_overwrite=true;
			pos_index = this->_itree.begin()->second;
        }
		else {
			return false;
		}
	}
	else {
		pos_index = _free_poses.front();
		_free_poses.pop_front();
	}
	index[pos_index].info = *index_rec;
    index[pos_index].is_init = true;

    if(!header->is_overwrite){
        index[pos_index].offset = header->pos_chunks;
        header->pos_chunks += header->chunk_size;
        header->addeded_chunks++;
		_itree.insert(std::make_pair(index_rec->maxTime, pos_index));
	}
	else {
		auto it = this->_itree.begin();
		_itree.erase(it);
		_itree.insert(std::make_pair(index_rec->maxTime, pos_index));
	}
	memcpy(this->chunks + index[pos_index].offset, buffer, sizeof(uint8_t)*header->chunk_size);

	header->minTime = std::min(header->minTime,ch->minTime);
	header->maxTime = std::max(header->maxTime, ch->maxTime);

    this->mmap->flush(get_header_offset(),sizeof(PageHeader));
    this->mmap->flush(get_index_offset()+sizeof(Page_ChunkIndex),sizeof(Page_ChunkIndex));
    auto offset=get_chunks_offset(header->chunk_per_storage)+size_t(this->chunks - index[pos_index].offset);
    this->mmap->flush(offset,sizeof(header->chunk_size));
    return true;
}

bool Page::is_full()const {
	return this->_free_poses.empty();
}

Cursor_ptr Page::get_chunks(const dariadb::IdArray&ids, dariadb::Time from, dariadb::Time to, dariadb::Flag flag) {
    std::lock_guard<std::mutex> lg(_locker);

	auto raw_ptr = new PageCursor(this,ids,from,to,flag );
	Cursor_ptr result{ raw_ptr };

	header->count_readers++;

	return result;
}

ChunksList Page::get_open_chunks() {
    std::lock_guard<std::mutex> lg(_locker);
	auto index_end = this->index + this->header->chunk_per_storage;
	auto index_it = this->index;
	ChunksList result;
	for (uint32_t pos = 0; index_it != index_end; ++index_it, ++pos) {
		if (!index_it->is_init) {
			continue;
		}
		if (!index_it->info.is_readonly) {
			index_it->is_init = false;
            auto ptr=new Chunk(index_it->info, this->chunks + index_it->offset, this->header->chunk_size);
            Chunk_Ptr c = Chunk_Ptr(ptr);
			result.push_back(c);
			index_it->is_init = false;
			this->header->addeded_chunks--;
			_free_poses.push_back(pos);
		}
	}
	return result;
}

void Page::dec_reader(){
    std::lock_guard<std::mutex> lg(_locker);
	header->count_readers--;
}

Cursor_ptr dariadb::storage::Page::chunksByIterval(const IdArray & ids, Flag flag, Time from, Time to)
{
	return get_chunks(ids, from, to, flag);
}

IdToChunkMap dariadb::storage::Page::chunksBeforeTimePoint(const IdArray & ids, Flag flag, Time timePoint)
{
	IdToChunkMap result;

	ChunksList ch_list;
	auto cursor = this->get_chunks(ids, header->minTime, timePoint, flag);
	if (cursor == nullptr) {
		return result;
	}
	cursor->readAll(&ch_list);

	for (auto&v : ch_list) {
		auto find_res = result.find(v->first.id);
		if (find_res == result.end()) {
			result.insert(std::make_pair(v->first.id, v));
		}
		else {
			if (find_res->second->maxTime < v->maxTime) {
				result[v->first.id] = v;
			}
		}
	}
	return result;
}
class CountOfIdCallback :public Cursor::Callback {
public:
	dariadb::IdSet ids;
	CountOfIdCallback() {
	}
	~CountOfIdCallback() {
	}

	virtual void call(Chunk_Ptr & ptr) override {
		if (ptr != nullptr) {
			ids.insert(ptr->first.id);
		}
	}
};

dariadb::IdArray dariadb::storage::Page::getIds()
{
	auto cursor = get_chunks(dariadb::IdArray{}, header->minTime, header->maxTime, 0);

	CountOfIdCallback*clbk_raw = new CountOfIdCallback;
	std::unique_ptr<Cursor::Callback> clbk{ clbk_raw };

	cursor->readAll(clbk.get());

	return dariadb::IdArray{ clbk_raw->ids.begin(),clbk_raw->ids.end() };
}
<|endoftext|>