create Wayland desktop environment that simply binds keyboard shortcuts - keyboard-shortcuts

There are dozens of questions on here asking how to create global keyboard bindings in a Wayland environment. Generally, the answer is "use your desktop environment" - an answer that is pretty much useless to most anyone asking the question.
So, in order to draw out a more useful answer, I ask how does one create a minimal Wayland desktop environment which can bind shortcut keys?
I've got Mutter running as my WM, and I'm using GNOME Do as a launcher. It's pretty much exactly the desktop environment I want, with the exception that I can't bind hotkeys.
I don't care if I have to write a 10k line C app to make this work. I just want to know how to proceed. How does GNOME bind keyboard shortcuts within Wayland? Where is the code for that? Where is the appropriate documentation for Wayland/Mutter?

After much research and experimentation, it looks like libevdev is maybe the right tool. I developed the program below as a proof-of-concept to bind Alt+X to launch xterm.
Unfortunately, it has to be run as root, so I'm thinking I need to somehow tie this into the local desktop session. For my purposes, it's probably good enough to just setuid using the root user and call it a day.
I'm also not convinced my keyboard detection heuristic is very good. I'm essentially looking for any device that has keys and a repeat rate, which on my system only matches my keyboard.
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <libevdev/libevdev.h>
#define DEVROOT "/dev/input/"
#define DEVROOT_LEN 12
#define PATH_LEN (DEVROOT_LEN + NAME_MAX)
int outerr(int, const char*);
struct libevdev* open_device(int);
bool kblike(struct libevdev*);
int main(int argc, char* argv[]) {
DIR* dir;
struct dirent* entry;
char path[PATH_LEN];
int fd, err;
struct libevdev* dev = NULL;
struct input_event ev;
bool key, rep, alt;
if (!(dir = opendir("/dev/input"))) {
return outerr(errno, "cannot enumerate devices");
}
// look for keyboard device
while (entry = readdir(dir)) {
if (DT_CHR == entry->d_type) {
sprintf(path, "/dev/input/%s", entry->d_name);
if (-1 == (fd = open(path, O_RDONLY|O_NONBLOCK))) {
return outerr(errno, "cannot read device");
}
if (dev = open_device(fd)) {
if (kblike(dev)) break;
libevdev_free(dev);
dev = NULL;
}
}
}
closedir(dir);
// check if keyboard was found
if (dev == NULL) {
return outerr(ENODEV, "could not detect keyboard");
} else do {
err = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
if (err == 0 && ev.type == EV_KEY) switch (ev.code) {
case KEY_LEFTALT:
alt = ev.value == 1;
break;
case KEY_X:
if (ev.value == 1 && alt) system("xterm");
break;
}
} while (err == 1 || err == 0 || err == -EAGAIN);
return 0;
}
int outerr(int errnum, const char* msg) {
fprintf(stderr, "%s (%s)\n", msg, strerror(errnum));
return errnum;
}
bool kblike(struct libevdev* dev) {
return libevdev_has_event_type(dev, EV_KEY)
&& libevdev_has_event_type(dev, EV_REP);
}
struct libevdev* open_device(int fd) {
struct libevdev* dev = libevdev_new();
int err;
if (dev == NULL) {
errno = ENOMEM;
} else if (0 > (err = libevdev_set_fd(dev, fd))) {
libevdev_free(dev);
dev = NULL;
errno = -err;
}
return dev;
}

Related

Trouble with Sending HTTP 1.1 GET request

I am trying to use PubNub to publish a message to a channel but I always get "Connection Failed!".
I know the structure of a publish URL is the following:
http://pubsub.pubnub.com/publish/<PUB-KEY>/<SUB-KEY>/0/<CHANNEL>/0/%22<MESSAGE>%22
I have tested the URL on Google Chrome and it works just fine.
The following is a C flavored code is running on an Arduino UNO + Adafruit HUZZAH CC3000 WIFI Breakout. This is not an Arduino hardware question per se, because I believe there is nothing wrong with my circuit. My issue is with the structure of the HTTP GET request created using Adafruit CC3000 Library.
I am having to go with sending a GET rather than using PubNub Arduino Library because it doesn't seem to support CC3000 WIFI module. They have a JSON WIFI and Ethernet examples but both do not communicate with the CC3000 WIFI module.
char PUBKEY[] = "XXXXXXX";
char SUBKEY[] = "XXXXXXX";
Adafruit_CC3000_Client client = cc3000.connectTCP(ip, 80);
if (client.connected())
{
client.print("GET /publish/");
client.print(PUBKEY);
client.print("/");
client.print(SUBKEY);
client.print("/0/");
client.print("MyPubChannel"); // Channel Name
client.print("/0/%22");
client.print("Hello World from Arduino!"); // Msg to publish
client.print("%22");
client.println(" HTTP/1.1");
client.print("Host: ");
client.println("pubsub.pubnub.com");
client.println();
}
else
{
Serial.println(F("Connection failed"));
}
I have read this page and understood that a correct GET request would be of the form:
GET /pub/WWW/TheProject.html HTTP/1.1
Host: www.w3.org
I am pretty sure there is something wrong with my GET request. Can anyone look at this and provide ANY hints as to what could be going wrong?
I am finally able to connect to PubNub using the code below.
#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"
#define ADAFRUIT_CC3000_IRQ 3 // MUST be an interrupt pin!
#define ADAFRUIT_CC3000_VBAT 5
#define ADAFRUIT_CC3000_CS 10
#define WLAN_SSID "XXXWifi"
#define WLAN_PASS "XXX"
#define WLAN_SECURITY WLAN_SEC_WPA2
#define IDLE_TIMEOUT_MS 3
uint32_t ip;
// PubNub Setup
#define WEBSITE "pubsub.pubnub.com"
#define WEBPAGE "/publish/"
char PUBKEY[] = "pub-XXXX";
char SUBKEY[] = "sub-XXXX";
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT, SPI_CLOCK_DIVIDER); // you can change this clock speed
Adafruit_CC3000_Client www;
void setup(void)
{
Serial.begin(115200);
/* Initialize the module */
Serial.print(F("\n Initializing WIFI Adapter..."));
if (!cc3000.begin())
{
Serial.println(F("Couldn't begin()! Check your wiring?"));
while(1);
}
Serial.print(F("Ready!\n"));
Serial.print(F("Connecting to [")); Serial.print(WLAN_SSID);
Serial.print(F("]..."));
if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
Serial.println(F("Failed!"));
while(1);
}
Serial.print(F("Connected!\n"));
//Wait for DHCP to complete
Serial.print(F("Waiting for DHCP..."));
while (!cc3000.checkDHCP())
{
delay(100); // ToDo: Insert a DHCP timeout!
}
Serial.print(F("Done!\n"));
Serial.print(F("Resolving PubNub..."));
ip = 0;
// Try looking up the website's IP address
while (ip == 0) {
if (! cc3000.getHostByName(WEBSITE, &ip)) {
Serial.println(F("Couldn't resolve!"));
}
delay(500);
}
Serial.print(F("Resolved!\n"));
www = cc3000.connectTCP(ip, 80);
for(int i=0;i<=50;i++)
{
char buffer[4];
dtostrf(i, 4, 0, buffer);
Publish("ch2", buffer);
}
/* You need to make sure to clean up after yourself or the CC3000 can freak out */
/* the next time your try to connect ... */
Serial.println(F("\n\nDisconnecting"));
www.close();
cc3000.disconnect();
}
void loop(void)
{
delay(1000);
}
void Publish(char* CH, char* MSG)
{
if (www.connected()) {
www.fastrprint(F("GET "));
www.fastrprint(WEBPAGE);
www.fastrprint(PUBKEY);
www.fastrprint(F("/"));
www.fastrprint(SUBKEY);
www.fastrprint(F("/0/"));
www.fastrprint(CH);
www.fastrprint(F("/0/%22"));
www.fastrprint(MSG);
www.fastrprint(F("%22"));
www.fastrprint(F(" HTTP/1.1\r\n"));
www.fastrprint(F("Host: "));
www.fastrprint(WEBSITE);
www.fastrprint(F("\r\n"));
www.fastrprint(F("\r\n"));
www.println();
} else {
Serial.println(F("Connection failed"));
return;
}
//Read data until either the connection is closed, or the idle timeout is reached.
unsigned long lastRead = millis();
while (www.connected() && (millis() - lastRead < IDLE_TIMEOUT_MS)) {
while (www.available()) {
char c = www.read();
//Serial.print(c);
lastRead = millis();
}
}
}
char* clean(char* MSG)
{
return MSG;
}

read multiple files with quite similar names c++

I am reading a file from current directory
ifstream myfile;
myfile.open("version1.1.hex");
Now a situation is arising that if user updates version then there will be version1.2.hex or version1.3.hex ..so on in the current directory, but one file at a time will be present. I want to write a code now which will cater this future need of reading different file.
I'm writing this code in C++/CLI.
Since file listings are a bit environment-dependant I am not sure if this is helpful to you,
but here is an example how to achieve your goal under the mircosoft regime.
What is needed is the FindFirstFile / FindNextFile calls which query all files matching the fileSearchKey. Then you can use the cFileName part of WIN32_FIND_DATAA as parameter to your open command
string fileSearchKey = "version*";
WIN32_FIND_DATAA fd;
bool bFirstRun = true;
bool bFinishedRun = false;
HANDLE h = INVALID_HANDLE_VALUE;
while (!bFinishedRun)
{
if (bFirstRun)
{
h = FindFirstFileA(fileSearchKey.c_str(), &fd);
bFirstRun = false;
} else
{
if (FindNextFileA(h, &fd) != FALSE)
{
// Abort with error because it has more than one file or decide for the most recent version
} else
{
bFinishedRun = true;
}
}
}
// Load file
ifstream myfile;
myfile.open(fd.cFileName);
This code will look in the directory and take the first file, then quit.
WARNING : this will work only on linux
#include <iostream>
#include <string>
#include <vector>
#include <stdio.h>
#include <cstring>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;
int main ()
{
char n[20];
unsigned char isFolder = 0x4;
unsigned char isFile = 0x8;
DIR *dir;
struct dirent *ent;
dir = opendir ("./");
if (dir != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
//folder sign
if(ent->d_type != isFolder && string(ent->d_name).find("version") != string::npos)
{
cout <<ent->d_name <<"\n";
// Your code
break;
}
}
closedir (dir);
} else {
/* could not open directory */
perror ("");
return 0;
}
cout << "=========" << endl;
}
In C++/CLI you should use the .net framework libraries for this. For instance you can use Directory::GetFiles.
using namespace System;
using namespace System::IO;
int main(array<System::String ^> ^args)
{
array<String^>^dirs = Directory::GetFiles(".", "version1.*.hex");
Collections::IEnumerator^ myEnum = dirs->GetEnumerator();
while (myEnum->MoveNext())
{
Console::WriteLine(myEnum->Current);
}
return 0;
}

SDL 2.0 Keyboard input issues

When I try to Poll a keydown event in SDL 2.0 and hold down a key, I get multiple keydown events, one after the other. When I try running the same program using SDL 1.2.15 (with minor changes as SDL 1.2.15 does not support SDL_Window), I do not have this issue. The keydown event only occurs once like it is supposed to. I even tried executing the program on a different computer to make sure that it was not a computer specific issue.
The relevant code is as follows:
#include <iostream>
#include <SDL.h>
using namespace std;
SDL_Event event;
SDL_Window* screen = NULL;
int main(int argc, char* args[])
{
if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
cout << "ERROR INIT";
return 0;
}
screen = SDL_CreateWindow("My Game Window",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_OPENGL);
bool quit = false;
while(!quit)
{
while(SDL_PollEvent(&event))
{
if(event.type == SDL_KEYDOWN)
{
cout << "KEY PRESSED!" << endl;
}
}
}
return 0;
}
Ignore SDL_KEYDOWN events where event.key.repeat != 0.

DllMain not being called from injected dll

I am attempting to inject a dll into an existing process using the LoadLibrary and CreateRemoteThread approach. All of my code is working beautifully except for the fact that DllMain is not being called for some reason.
I've wracked my brains and done as much internet research as I can but none of the suggestions have helped.
When I statically load the dll into a sample project it works beautifully.
When I dynamically load the dll into a sample project using LoadLibrary, it works beautifully.
The only time that DllMain is not called is when I'm trying to inject it into a process using the LoadLibrary and CreateRemoteThread approach. I'm at my wits end!
I have verified that the dll is loaded into the notepad.exe process from the injector because the SimpleDLL.dll file is locked and cannot be deleted or overwritten until I close notepad.exe.
Fyi, my ide is Microsoft Visual Studio 2010 Ultimate.
// SimpleDLL.h
#pragma once
#include "Stdafx.h"
#ifdef COMPILE_MYLIBRARY
#define MYLIBRARY_EXPORT __declspec(dllexport)
#else
#define MYLIBRARY_EXPORT __declspec(dllimport)
#endif
.
//SimpleDLL.cpp
#include "Stdafx.h"
#include "SimpleDll.h"
extern "C" int __stdcall DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) {
printf("SimpleDll: DllMain called.\n");
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
printf("SimpleDll: DLL_PROCESS_ATTACH.\n");
break;
case DLL_PROCESS_DETACH:
printf("SimpleDll: DLL_PROCESS_DETACH.\n");
break;
case DLL_THREAD_ATTACH:
printf("SimpleDll: DLL_THREAD_ATTACH.\n");
break;
case DLL_THREAD_DETACH:
printf("SimpleDll: DLL_THREAD_DETACH.\n");
break;
}
return TRUE;
};
SimpleDLLCaller.cpp - This runs correctly and prints out the messages from DllMain. This is a project I built to verify that the dll is constructed correctly. From the output that I can see, it appears to be operating correctly when loaded in this way.
// SimpleDLLCaller.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
LoadLibraryA( "C:\\SimpleDll.dll" );
_getch();
return 0;
}
Output from SimpleDllCaller:
SimpleDll: DllMain called.
SimpleDll: DLL_PROCESS_ATTACH.
Simple Injector.cpp - This is a separate project from SimpleDLLCaller and compiles/runs successfully without warnings (as long as notepad.exe is already running) but no messages from DllMain are displayed.
// Simple Injector.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <conio.h>
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#define CREATE_THREAD_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ)
BOOL Inject(DWORD pID, const char * dll_name )
{
HANDLE targetProcess, createdThread;
//HMODULE hLib;
char buf[50] = {0};
LPVOID myRemoteString, LoadLibAddy;
if( ! pID )
{
return FALSE;
}
targetProcess = OpenProcess( CREATE_THREAD_ACCESS, FALSE, pID );
if( ! targetProcess )
{
sprintf_s(buf, "OpenProcess() failed: %d", GetLastError());
MessageBox(NULL, buf, "Loader", MB_OK);
printf(buf);
return false;
}
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if( ! LoadLibAddy )
{
printf( "ERROR: Problems with GetProcAddress. Error code: %d\n", GetLastError() );
return false;
}
// Allocate space in the process for the dll
myRemoteString = (LPVOID)VirtualAllocEx( targetProcess, NULL, strlen(dll_name), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if( ! myRemoteString )
{
printf( "ERROR: Problems with VirtualAllocEx. Error code: %d\n", GetLastError() );
return false;
}
// Write the string name of the dll in the memory allocated
if( ! WriteProcessMemory( targetProcess, (LPVOID)myRemoteString, dll_name, strlen(dll_name), NULL) )
{
printf( "ERROR: Problems with WriteProcessMemory. Error code: %d\n", GetLastError() );
return false;
}
// Load the dll
createdThread = CreateRemoteThread( targetProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)myRemoteString, NULL, NULL);
if( ! createdThread )
{
printf( "ERROR: Problems with CreateRemoteThread. Error code: %d\n", GetLastError() );
return false;
}
WaitForSingleObject(createdThread, INFINITE);
// Free the memory that is not being using anymore.
if( myRemoteString != NULL ) VirtualFreeEx( targetProcess, myRemoteString, 0, MEM_RELEASE );
if( createdThread != NULL ) CloseHandle( createdThread );
if( targetProcess != NULL ) CloseHandle( targetProcess );
//VirtualFreeEx(hProcess , (LPVOID)Memory , 0, MEM_RELEASE);
return true;
}
DWORD GetTargetThreadIDFromProcName(const char *ProcName)
{
PROCESSENTRY32 pe;
HANDLE thSnapShot;
BOOL retval, ProcFound = false;
thSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(thSnapShot == INVALID_HANDLE_VALUE)
{
//MessageBox(NULL, "Error: Unable <strong class="highlight">to</strong> create toolhelp snapshot!", "2MLoader", MB_OK);
printf("Error: Unable to create toolhelp snapshot!");
return false;
}
pe.dwSize = sizeof(PROCESSENTRY32);
retval = Process32First(thSnapShot, &pe);
while(retval)
{
if( !strcmp(pe.szExeFile, ProcName) )
{
return pe.th32ProcessID;
}
retval = Process32Next(thSnapShot, &pe);
}
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
// Retrieve process ID
DWORD pID = GetTargetThreadIDFromProcName("notepad.exe");
if( !pID )
{
printf( "ERROR: Could not find any process for notepad.exe.\n");
_getch();
return 0;
}
// Get the dll's full path name
char buf[MAX_PATH] = {0};
// GetFullPathName("..\\SimpleDLL.dll", MAX_PATH, buf, NULL);
sprintf_s(buf, "C:\\SimpleDLL.dll");
printf( "Dll path = %s\n", buf );
// Inject our main dll
if(!Inject(pID, buf))
{
printf("Dll not loaded.");
}
else
{
printf("Dll loaded.");
}
_getch();
return 0;
}
Output from Simple Injector:
Dll path = C:\SimpleDLL.dll
Dll loaded.
Please tell me what I'm doing wrong here. Any feedback is welcome.
Your DLL may be correctly Loaded. Don't expect your printf to do do anything. If you want a proof, write your message in a text file, or use MessageBox, or use Process Explorer.

Controlling the volume of other applications

I am trying to make an app that controls the volume of another process using the Windows 7 Audio API.
What I'm looking for is the ISimpleAudioVolume for the session used by the other process.
I have tried using the IAudioSessionEnumerator but it will only give me the IAudioSessionControl2 of the session. Using the IAudioSessionControl I have managed to receive notifications when I change the volume through sndvol but not change it myself.
I have also tried using GetSimpleAudioVolume() from IAudioSessionManager but it will only give me sessions within the current process.
How do you do it? It should be possible since sndvol is doing this.
Here is an example of muting another process using Core Audio API.
#include <windows.h>
#include <iostream>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <Audiopolicy.h>
#include <comdef.h>
#include <comip.h>
#define CHECK_HR(hr) \
if(FAILED(hr)) { \
std::cout << "error" << std::endl; \
return 0; \
}
_COM_SMARTPTR_TYPEDEF(IMMDevice, __uuidof(IMMDevice));
_COM_SMARTPTR_TYPEDEF(IMMDeviceEnumerator, __uuidof(IMMDeviceEnumerator));
_COM_SMARTPTR_TYPEDEF(IAudioSessionManager2, __uuidof(IAudioSessionManager2));
_COM_SMARTPTR_TYPEDEF(IAudioSessionManager2, __uuidof(IAudioSessionManager2));
_COM_SMARTPTR_TYPEDEF(IAudioSessionEnumerator, __uuidof(IAudioSessionEnumerator));
_COM_SMARTPTR_TYPEDEF(IAudioSessionControl2, __uuidof(IAudioSessionControl2));
_COM_SMARTPTR_TYPEDEF(IAudioSessionControl, __uuidof(IAudioSessionControl));
_COM_SMARTPTR_TYPEDEF(ISimpleAudioVolume, __uuidof(ISimpleAudioVolume));
IAudioSessionManager2Ptr CreateSessionManager()
{
HRESULT hr = S_OK;
IMMDevicePtr pDevice;
IMMDeviceEnumeratorPtr pEnumerator;
IAudioSessionManager2Ptr pSessionManager;
// Create the device enumerator.
CHECK_HR(hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(void**)&pEnumerator));
// Get the default audio device.
CHECK_HR(hr = pEnumerator->GetDefaultAudioEndpoint(
eRender, eConsole, &pDevice));
// Get the session manager.
CHECK_HR(hr = pDevice->Activate(
__uuidof(IAudioSessionManager2), CLSCTX_ALL,
NULL, (void**)&pSessionManager));
return pSessionManager;
}
bool MuteProcess(DWORD processId) {
IAudioSessionManager2Ptr mgr = CreateSessionManager();
if (!mgr) {
return false;
}
IAudioSessionEnumeratorPtr enumerator;
if (SUCCEEDED(mgr->GetSessionEnumerator(&enumerator))) {
int sessionCount;
if (SUCCEEDED(enumerator->GetCount(&sessionCount))) {
for (int i = 0; i < sessionCount; i++) {
IAudioSessionControlPtr control;
if (SUCCEEDED(enumerator->GetSession(i, &control))) {
IAudioSessionControl2Ptr control2;
if (SUCCEEDED(control->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&control2))) {
DWORD foundProcessId;
if (SUCCEEDED(control2->GetProcessId(&foundProcessId))) {
if (foundProcessId == processId) {
ISimpleAudioVolumePtr volume;
if (SUCCEEDED(control2->QueryInterface(_uuidof(ISimpleAudioVolume), (void**)&volume))) {
if (SUCCEEDED(volume->SetMute(TRUE, 0))) {
return true;
}
}
}
}
}
}
}
}
}
return false;
}
int _tmain(int argc, _TCHAR* argv[]){
CoInitialize(NULL);
DWORD processId = 11944;
MuteProcess(processId);
return 0;
}
There is an MSDN forum question and Blog Post about this very question. Hope this helps.
According to Larry Osterman
"There is no publicly documented mechanism for doing what you're trying to do."