mirror of
https://github.com/debauchee/barrier.git
synced 2024-12-29 14:12:37 +03:00
d2135af0d9
CServer to the primary screen when the configuration changes so it can make necessary adjustments (the win32 primary screen must tell the hook dll about the new jump zones). changed includes of some std c++ library files to go through our own include files. these wrap the include with stuff to keep vc++ quiet when compiling at warning level 4, which is what it does now. it also works around missing <istream> and <ostream> on g++2.96. added missing std:: where necessary. g++ doesn't really support namespaces so it lets references without the namespace slip through. added workaround or fix. not sure if istringstream::str(string) should reset eofbit. it does on g++ but does not on vc++. added clear() after str() so it works either way. added low-level keyboard hook to win32. if available (it's only available on NT SP3 and up) it allows us to catch and handle alt+tab, alt+esc, ctrl+esc, and windows key hot keys. i think that leaves only ctrl+alt+del and accessibility functions uncaught on those systems.
409 lines
8.6 KiB
C++
409 lines
8.6 KiB
C++
#include "CConfig.h"
|
|
#include "stdistream.h"
|
|
#include "stdostream.h"
|
|
#include <assert.h>
|
|
|
|
//
|
|
// CConfig
|
|
//
|
|
|
|
CConfig::CConfig()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
CConfig::~CConfig()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
void CConfig::addScreen(const CString& name)
|
|
{
|
|
if (m_map.count(name) != 0) {
|
|
assert(0 && "name already in map"); // FIXME -- throw instead
|
|
}
|
|
m_map.insert(std::make_pair(name, CCell()));
|
|
}
|
|
|
|
void CConfig::removeScreen(const CString& name)
|
|
{
|
|
CCellMap::iterator index = m_map.find(name);
|
|
if (index == m_map.end()) {
|
|
assert(0 && "name not in map"); // FIXME -- throw instead
|
|
}
|
|
|
|
// remove from map
|
|
m_map.erase(index);
|
|
|
|
// disconnect
|
|
for (index = m_map.begin(); index != m_map.end(); ++index) {
|
|
CCell& cell = index->second;
|
|
for (SInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i)
|
|
if (cell.m_neighbor[i] == name) {
|
|
cell.m_neighbor[i].erase();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CConfig::removeAllScreens()
|
|
{
|
|
m_map.clear();
|
|
}
|
|
|
|
void CConfig::connect(const CString& srcName,
|
|
EDirection srcSide,
|
|
const CString& dstName)
|
|
{
|
|
// find source cell
|
|
CCellMap::iterator index = m_map.find(srcName);
|
|
if (index == m_map.end()) {
|
|
assert(0 && "name not in map"); // FIXME -- throw instead
|
|
}
|
|
|
|
// connect side (overriding any previous connection)
|
|
index->second.m_neighbor[srcSide - kFirstDirection] = dstName;
|
|
}
|
|
|
|
void CConfig::disconnect(const CString& srcName,
|
|
EDirection srcSide)
|
|
{
|
|
// find source cell
|
|
CCellMap::iterator index = m_map.find(srcName);
|
|
if (index == m_map.end()) {
|
|
assert(0 && "name not in map"); // FIXME -- throw instead
|
|
}
|
|
|
|
// disconnect side
|
|
index->second.m_neighbor[srcSide - kFirstDirection].erase();
|
|
}
|
|
|
|
bool CConfig::isValidScreenName(const CString& name) const
|
|
{
|
|
// name is valid if matches validname
|
|
// name ::= [A-Za-z0-9] | [A-Za-z0-9][-A-Za-z0-9]*[A-Za-z0-9]
|
|
// domain ::= . name
|
|
// validname ::= name domain*
|
|
|
|
// check each dot separated part
|
|
CString::size_type b = 0;
|
|
for (;;) {
|
|
// find end of part
|
|
CString::size_type e = name.find('.', b);
|
|
if (e == CString::npos) {
|
|
e = name.size();
|
|
}
|
|
|
|
// part may not be empty
|
|
if (e - b < 1) {
|
|
return false;
|
|
}
|
|
|
|
// check first and last characters
|
|
if (!isalnum(name[b]) || !isalnum(name[e - 1])) {
|
|
return false;
|
|
}
|
|
|
|
// check interior characters
|
|
for (CString::size_type i = b; i < e; ++i) {
|
|
if (!isalnum(name[i]) && name[i] != '-') {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// next part
|
|
if (e == name.size()) {
|
|
// no more parts
|
|
break;
|
|
}
|
|
b = e + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CConfig::const_iterator CConfig::begin() const
|
|
{
|
|
return const_iterator(m_map.begin());
|
|
}
|
|
|
|
CConfig::const_iterator CConfig::end() const
|
|
{
|
|
return const_iterator(m_map.end());
|
|
}
|
|
|
|
bool CConfig::isScreen(const CString& name) const
|
|
{
|
|
return (m_map.count(name) > 0);
|
|
}
|
|
|
|
CString CConfig::getNeighbor(const CString& srcName,
|
|
EDirection srcSide) const
|
|
{
|
|
// find source cell
|
|
CCellMap::const_iterator index = m_map.find(srcName);
|
|
if (index == m_map.end()) {
|
|
assert(0 && "name not in map"); // FIXME -- throw instead
|
|
}
|
|
|
|
// return connection
|
|
return index->second.m_neighbor[srcSide - kFirstDirection];
|
|
}
|
|
|
|
const char* CConfig::dirName(EDirection dir)
|
|
{
|
|
static const char* s_name[] = { "left", "right", "top", "bottom" };
|
|
return s_name[dir - kFirstDirection];
|
|
}
|
|
|
|
bool CConfig::readLine(std::istream& s, CString& line)
|
|
{
|
|
s >> std::ws;
|
|
while (std::getline(s, line)) {
|
|
// strip comments and then trailing whitespace
|
|
CString::size_type i = line.rfind('#');
|
|
if (i != CString::npos) {
|
|
line.erase(i);
|
|
}
|
|
i = line.find_last_not_of(" \t");
|
|
if (i != CString::npos) {
|
|
line.erase(i + 1);
|
|
}
|
|
|
|
// return non empty line
|
|
if (!line.empty()) {
|
|
return true;
|
|
}
|
|
s >> std::ws;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CConfig::readSection(std::istream& s)
|
|
{
|
|
static const char s_section[] = "section:";
|
|
static const char s_screens[] = "screens";
|
|
static const char s_links[] = "links";
|
|
|
|
CString line;
|
|
if (!readLine(s, line)) {
|
|
// no more sections
|
|
return;
|
|
}
|
|
|
|
// should be a section header
|
|
if (line.find(s_section) != 0) {
|
|
throw XConfigRead("found data outside section");
|
|
}
|
|
|
|
// get section name
|
|
CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1);
|
|
if (i == CString::npos) {
|
|
throw XConfigRead("section name is missing");
|
|
}
|
|
CString name = line.substr(i);
|
|
i = name.find_first_of(" \t");
|
|
if (i != CString::npos) {
|
|
throw XConfigRead("unexpected data after section name");
|
|
}
|
|
|
|
// read section
|
|
if (name == s_screens) {
|
|
readSectionScreens(s);
|
|
}
|
|
else if (name == s_links) {
|
|
readSectionLinks(s);
|
|
}
|
|
else {
|
|
throw XConfigRead("unknown section name");
|
|
}
|
|
}
|
|
|
|
void CConfig::readSectionScreens(std::istream& s)
|
|
{
|
|
CString line;
|
|
CString name;
|
|
while (readLine(s, line)) {
|
|
// check for end of section
|
|
if (line == "end") {
|
|
return;
|
|
}
|
|
|
|
// see if it's the next screen
|
|
if (line[line.size() - 1] == ':') {
|
|
// strip :
|
|
name = line.substr(0, line.size() - 1);
|
|
|
|
// verify validity of screen name
|
|
if (!isValidScreenName(name)) {
|
|
throw XConfigRead("invalid screen name");
|
|
}
|
|
|
|
// add the screen to the configuration
|
|
addScreen(name);
|
|
}
|
|
else if (name.empty()) {
|
|
throw XConfigRead("argument before first screen");
|
|
}
|
|
else {
|
|
throw XConfigRead("unknown argument");
|
|
}
|
|
}
|
|
throw XConfigRead("unexpected end of screens section");
|
|
}
|
|
|
|
void CConfig::readSectionLinks(std::istream& s)
|
|
{
|
|
CString line;
|
|
CString screen;
|
|
while (readLine(s, line)) {
|
|
// check for end of section
|
|
if (line == "end") {
|
|
return;
|
|
}
|
|
|
|
// see if it's the next screen
|
|
if (line[line.size() - 1] == ':') {
|
|
// strip :
|
|
screen = line.substr(0, line.size() - 1);
|
|
|
|
// verify we known about the screen
|
|
if (!isScreen(screen)) {
|
|
throw XConfigRead("unknown screen name");
|
|
}
|
|
}
|
|
else if (screen.empty()) {
|
|
throw XConfigRead("argument before first screen");
|
|
}
|
|
else {
|
|
// parse argument: `<name>=<value>'
|
|
CString::size_type i = line.find_first_of(" \t=");
|
|
if (i == 0) {
|
|
throw XConfigRead("missing argument name");
|
|
}
|
|
if (i == CString::npos) {
|
|
throw XConfigRead("missing = in argument");
|
|
}
|
|
CString name = line.substr(0, i);
|
|
i = line.find_first_not_of(" \t", i);
|
|
if (i == CString::npos || line[i] != '=') {
|
|
throw XConfigRead("missing = in argument");
|
|
}
|
|
i = line.find_first_not_of(" \t", i + 1);
|
|
CString value;
|
|
if (i != CString::npos) {
|
|
value = line.substr(i);
|
|
}
|
|
|
|
// handle argument
|
|
if (name == "left") {
|
|
if (!isScreen(value)) {
|
|
throw XConfigRead("unknown screen");
|
|
}
|
|
connect(screen, kLeft, value);
|
|
}
|
|
else if (name == "right") {
|
|
if (!isScreen(value)) {
|
|
throw XConfigRead("unknown screen");
|
|
}
|
|
connect(screen, kRight, value);
|
|
}
|
|
else if (name == "up") {
|
|
if (!isScreen(value)) {
|
|
throw XConfigRead("unknown screen");
|
|
}
|
|
connect(screen, kTop, value);
|
|
}
|
|
else if (name == "down") {
|
|
if (!isScreen(value)) {
|
|
throw XConfigRead("unknown screen");
|
|
}
|
|
connect(screen, kBottom, value);
|
|
}
|
|
else {
|
|
// unknown argument
|
|
throw XConfigRead("unknown argument");
|
|
}
|
|
}
|
|
}
|
|
throw XConfigRead("unexpected end of links section");
|
|
}
|
|
|
|
|
|
//
|
|
// CConfig I/O
|
|
//
|
|
|
|
std::istream& operator>>(std::istream& s, CConfig& config)
|
|
{
|
|
// FIXME -- should track line and column to improve error reporting
|
|
|
|
CConfig tmp;
|
|
while (s) {
|
|
tmp.readSection(s);
|
|
}
|
|
config = tmp;
|
|
return s;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& s, const CConfig& config)
|
|
{
|
|
// screens section
|
|
s << "section: screens" << std::endl;
|
|
for (CConfig::const_iterator screen = config.begin();
|
|
screen != config.end(); ++screen) {
|
|
s << "\t" << screen->c_str() << ":" << std::endl;
|
|
}
|
|
s << "end" << std::endl;
|
|
|
|
// links section
|
|
CString neighbor;
|
|
s << "section: links" << std::endl;
|
|
for (CConfig::const_iterator screen = config.begin();
|
|
screen != config.end(); ++screen) {
|
|
s << "\t" << screen->c_str() << ":" << std::endl;
|
|
|
|
neighbor = config.getNeighbor(*screen, CConfig::kLeft);
|
|
if (!neighbor.empty()) {
|
|
s << "\t\tleft=" << neighbor.c_str() << std::endl;
|
|
}
|
|
|
|
neighbor = config.getNeighbor(*screen, CConfig::kRight);
|
|
if (!neighbor.empty()) {
|
|
s << "\t\tright=" << neighbor.c_str() << std::endl;
|
|
}
|
|
|
|
neighbor = config.getNeighbor(*screen, CConfig::kTop);
|
|
if (!neighbor.empty()) {
|
|
s << "\t\tup=" << neighbor.c_str() << std::endl;
|
|
}
|
|
|
|
neighbor = config.getNeighbor(*screen, CConfig::kBottom);
|
|
if (!neighbor.empty()) {
|
|
s << "\t\tdown=" << neighbor.c_str() << std::endl;
|
|
}
|
|
}
|
|
s << "end" << std::endl;
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
//
|
|
// CConfig I/O exceptions
|
|
//
|
|
|
|
XConfigRead::XConfigRead(const CString& error) : m_error(error)
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
XConfigRead::~XConfigRead()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
CString XConfigRead::getWhat() const throw()
|
|
{
|
|
return m_error;
|
|
}
|