blob: 19b259fbfbcd6cefc77e1d2abc4932163d168883 [file] [log] [blame]
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program 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 General Public License for more details.
#include "android/emulator-check/PlatformInfo.h"
#include "android/base/StringView.h"
#ifdef __linux__
#include "android/base/system/System.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <algorithm>
#include <memory>
#include <vector>
#include <ctype.h>
#endif
namespace android {
#ifdef __linux__
namespace {
struct XDeleter {
void operator()(Display* d) const { XCloseDisplay(d); }
void operator()(void* ptr) const { XFree(ptr); }
};
}
template <class T>
using XPtr = std::unique_ptr<T, XDeleter>;
// The following code is a slightly modified wmctrl code taken from
// https://github.com/geekless/wmctrl - GNU GPL 2 license
static std::vector<char> getXProperty(Display* disp,
Window win,
Atom xa_prop_type,
const char* prop_name) {
Atom xa_prop_name = XInternAtom(disp, prop_name, False);
/* 1024 explanation (XGetWindowProperty manpage):
*
* long_length = Specifies the length in 32-bit multiples of the
* data to be retrieved.
*
* NOTE: see
* http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html
* In particular:
*
* When the X window system was ported to 64-bit architectures, a
* rather peculiar design decision was made. 32-bit quantities such
* as Window IDs, atoms, etc, were kept as longs in the client side
* APIs, even when long was changed to 64 bits.
*
*/
Atom xa_ret_type;
int ret_format;
unsigned long ret_nitems;
unsigned long ret_bytes_after;
unsigned char* ret_prop;
if (XGetWindowProperty(disp, win, xa_prop_name, 0, 1024, False,
xa_prop_type, &xa_ret_type, &ret_format, &ret_nitems,
&ret_bytes_after, &ret_prop) != Success) {
// Cannot get the property.
return {};
}
// Create a RAII object to make sure |ret_prop| doesn't leak.
const XPtr<unsigned char> retPropPtr(ret_prop);
if (xa_ret_type != xa_prop_type) {
return {};
}
unsigned long tmp_size = (ret_format / 8) * ret_nitems;
/* Correct 64 Architecture implementation of 32 bit data */
if (ret_format == 32) {
tmp_size *= sizeof(long) / 4;
}
return std::vector<char>(ret_prop, ret_prop + tmp_size);
}
static std::string getLinuxWindowManagerName() {
const XPtr<Display> disp(XOpenDisplay(nullptr));
if (!disp) {
return {};
}
std::vector<char> supWindowPtrAsChars =
getXProperty(disp.get(), DefaultRootWindow(disp.get()),
XA_WINDOW, "_NET_SUPPORTING_WM_CHECK");
if (supWindowPtrAsChars.empty()) {
supWindowPtrAsChars =
getXProperty(disp.get(), DefaultRootWindow(disp.get()),
XA_CARDINAL, "_WIN_SUPPORTING_WM_CHECK");
if (supWindowPtrAsChars.empty()) {
// Cannot get window manager info properties.
return {};
}
}
if (supWindowPtrAsChars.size() < sizeof(Window)) {
// Bad size returned, has to be at least of a Window type size
return {};
}
const auto supWindow =
reinterpret_cast<const Window*>(supWindowPtrAsChars.data());
std::vector<char> wmName = getXProperty(
disp.get(), *supWindow,
XInternAtom(disp.get(), "UTF8_STRING", False), "_NET_WM_NAME");
if (wmName.empty()) {
wmName =
getXProperty(disp.get(), *supWindow, XA_STRING, "_NET_WM_NAME");
if (wmName.empty()) {
// Cannot get name of the window manager
return {};
}
}
return std::string(wmName.begin(), wmName.end());
}
static std::string getLinuxDesktopEnvironmentName() {
using android::base::System;
auto xdg = System::get()->envGet("XDG_CURRENT_DESKTOP");
std::transform(xdg.begin(), xdg.end(), xdg.begin(), tolower);
if (xdg.empty()) {
// try another one...
const auto gdm = System::get()->envGet("GDMSESSION");
if (gdm.find("kde") != std::string::npos) {
return "kde";
} else if (gdm.empty()) {
return "mate";
}
}
if (xdg == "x-cinnamon") {
return "cinnamon";
}
// the rest of them are pretty much the same as their name
return xdg;
}
#endif
std::string getWindowManagerName() {
#ifdef _WIN32
return android::base::StringView("Windows");
#elif defined(__APPLE__)
return android::base::StringView("Mac");
#elif defined(__linux__)
return getLinuxWindowManagerName();
#else
#error Unsupported platform
#endif
}
std::string getDesktopEnvironmentName() {
#ifdef _WIN32
return android::base::StringView("Windows");
#elif defined(__APPLE__)
return android::base::StringView("Mac");
#elif defined(__linux__)
return getLinuxDesktopEnvironmentName();
#else
#error Unsupported platform
#endif
}
} // namespace android