commit 5634b5d5d73c8b2a58313102dcfacf5501e29fa5 Author: IoIxD Date: Tue Nov 18 18:47:11 2025 -0700 ok diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e7f87e3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.25) +project( + mdebase +) + +add_subdirectory(mdm) +add_subdirectory(milkwm) +add_subdirectory(mpanel) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d6a83e7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2025, Milsko Desktop Experience development team +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8764fdd --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Base package for MDE diff --git a/mdm/CMakeLists.txt b/mdm/CMakeLists.txt new file mode 100644 index 0000000..7ff4ebe --- /dev/null +++ b/mdm/CMakeLists.txt @@ -0,0 +1,29 @@ +include(MDEBase) +include(MDEPkgConfig) +setup_project(mdm ${CMAKE_INSTALL_SBINDIR}) + +pkg_config_add(mdm x11) +pkg_config_add(mdm inih) + +target_link_libraries( + mdm + PRIVATE + pam pthread +) + +include(GNUInstallDirs) +configure_file(mdmrc.in mdmrc) + +install( + FILES pam + RENAME mdm + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/examples/pam.d +) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/mdmrc + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/examples/mdm +) +install( + FILES mdm.png + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mdm +) diff --git a/mdm/config.c b/mdm/config.c new file mode 100644 index 0000000..bfb3f9e --- /dev/null +++ b/mdm/config.c @@ -0,0 +1,19 @@ +#include "mdm.h" + +#include + +char* config_picture = NULL; + +static int dumper(void* user, const char* section, const char* name, const char* value) { + (void)user; + + if(strcmp(section, "MDM Config") != 0) return 1; + + if(strcmp(name, "Picture") == 0) config_picture = MDEStringDuplicate(value); + + return 1; +} + +void parse_config(void) { + ini_parse(CONFDIR "/mdm/mdmrc", dumper, NULL); +} diff --git a/mdm/login.c b/mdm/login.c new file mode 100644 index 0000000..aa22177 --- /dev/null +++ b/mdm/login.c @@ -0,0 +1,274 @@ +#include "mdm.h" + +#include +#include +#include +#include +#include +#include +#include + +MwWidget root; +MwWidget window, sesscombo, usercombo, passentry, error; + +typedef struct session { + char* key; + session_environment_t* value; +} session_t; + +session_t* sessions = NULL; +char* name_line = NULL; +char* try_exec_line = NULL; +char* exec_line = NULL; +char* desktop_names_line = NULL; + +static void add_user(const char* name, void* user) { + MwComboBoxAdd(user, -1, name); +} + +static int dumper(void* user, const char* section, const char* name, const char* value) { + if(strcmp(section, "Desktop Entry") != 0) return 1; + + if(strcmp(name, "Name") == 0) { + name_line = MDEStringDuplicate(value); + } else if(strcmp(name, "TryExec") == 0) { + try_exec_line = MDEStringDuplicate(value); + } else if(strcmp(name, "Exec") == 0) { + exec_line = MDEStringDuplicate(value); + } else if(strcmp(name, "DesktopNames") == 0) { + desktop_names_line = MDEStringDuplicate(value); + } + + return 1; +} + +static void add_session(session_type_t session_type, const char* path, int dir, int symlink, void* user) { + int m = 1; + session_environment_t* value = malloc(sizeof(session_environment_t)); + + name_line = NULL; + exec_line = NULL; + try_exec_line = NULL; + desktop_names_line = NULL; + + if(dir || symlink) return; + + ini_parse(path, dumper, user); + + if(name_line != NULL && (exec_line != NULL || try_exec_line != NULL)) { + int i; + for(i = 0; i < shlen(sessions); i++) { + if(strcmp(sessions[i].key, name_line) == 0) break; + } + + if(i == shlen(sessions)) { + MwComboBoxAdd(user, -1, name_line); + + value->run = exec_line; + value->try_run = try_exec_line; + value->session_type = session_type; + value->desktop_names = desktop_names_line; + shput(sessions, name_line, value); + + m = 0; + } + } + + if(name_line != NULL) free(name_line); + if(desktop_names_line != NULL) free(desktop_names_line); + if(m && try_exec_line != NULL) free(try_exec_line); + if(m && exec_line != NULL) free(exec_line); +} + +static void add_session_x(const char* path, int dir, int symlink, void* user) { + add_session(MDM_SESSION_TYPE_X, path, dir, symlink, user); +} +static void add_session_wayland(const char* path, int dir, int symlink, void* user) { + add_session(MDM_SESSION_TYPE_WAYLAND, path, dir, symlink, user); +} +static int conversation(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata) { + int i; + + *resp = malloc(num_msg * sizeof(struct pam_response)); + + for(i = 0; i < num_msg; i++) { + if(msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) { + const char* txt = MwGetText(passentry, MwNtext); + if(txt == NULL) txt = ""; + + resp[i]->resp_retcode = PAM_SUCCESS; + resp[i]->resp = MDEStringDuplicate(txt); + } else { + return PAM_CONV_ERR; + } + } + + return PAM_SUCCESS; +} + +static void try_login(MwWidget handle, void* user, void* client) { + const char* username; + const char* place; + struct pam_conv conv; + pam_handle_t* pam; + int err; + int err2; + int ret; + char err_str[255]; + int print_error = 0; + int i, n; + + username = MwComboBoxGet(usercombo, MwGetInteger(usercombo, MwNvalue)); + conv.conv = conversation; + conv.appdata_ptr = NULL; + err = 1; + + (void)handle; + (void)user; + (void)client; + + ret = pam_start("mdm", username, &conv, &pam); + if(ret != PAM_SUCCESS) { + place = "Error calling pam_start"; + goto cleanup; + } + + ret = pam_authenticate(pam, 0); + if(ret != PAM_SUCCESS) { + place = "Error calling pam_authenticate"; + goto cleanup; + } + + ret = pam_acct_mgmt(pam, 0); + if(ret != PAM_SUCCESS) { + place = "Error calling pam_acct_mgmt"; + goto cleanup; + } + + ret = pam_open_session(pam, 0); + if(ret != PAM_SUCCESS) { + place = "Error calling pam_open_session"; + goto cleanup; + } +cleanup: + pam_close_session(pam, 0); + + // if pam_end fails but we have another error, we care more about the error at hand + // then whether pam successfully ended. + err2 = pam_end(pam, 0); + + if(err && ret != PAM_SUCCESS) { + MwSetText(passentry, MwNtext, ""); + snprintf(err_str, 255, "%s: %s", place, pam_strerror(pam, err)); + print_error = 1; + } else if(err2) { + MwSetText(passentry, MwNtext, ""); + snprintf(err_str, 255, "Error calling pam_end: %s", pam_strerror(pam, err2)); + print_error = 1; + } + if(print_error) { + n = 0; + for(i = 0; err_str[i] != '\0' && i <= 255; i++ && n++) { + if(err_str[i] == ' ' && n >= 40) { + err_str[i] = '\n'; + n = 0; + } + } + MwSetText(error, MwNtext, err_str); + } else { + printf("Logging in as %s\n", username); + struct passwd* pwd = getpwnam(username); + const char* session = MwComboBoxGet(sesscombo, MwGetInteger(sesscombo, MwNvalue)); + + uid = pwd->pw_uid; + gid = pwd->pw_gid; + env = shget(sessions, session); + + MwDestroyWidget(root); + } +} + +void login_window(void) { + MwWidget pic, userlabel, passlabel, mainsep, sesslabel, fieldsep, shutdown, reboot, ok; + MwLLPixmap p; + void* out; + int i; + + pthread_mutex_lock(&xmutex); + + MwLibraryInit(); + + root = MwCreateWidget(NULL, "root", NULL, 0, 0, 0, 0); + + window = MwVaCreateWidget(MwWindowClass, "login", root, (x_width() - 366) / 2, (x_height() - 200) / 2, 366, 250, + MwNtitle, "login", + NULL); + p = config_picture == NULL ? NULL : MwLoadImage(window, config_picture); + pic = MwVaCreateWidget(MwImageClass, "image", window, 10, 10, 80, 137, + MwNhasBorder, 1, + NULL); + userlabel = MwVaCreateWidget(MwLabelClass, "userlabel", window, 95, 10, MwTextWidth(window, "User:"), 16, + MwNtext, "User:", + NULL); + usercombo = MwCreateWidget(MwComboBoxClass, "usercombo", window, 95, 10 + 16 + 5, 265, 18); + passlabel = MwVaCreateWidget(MwLabelClass, "passlabel", window, 95, 10 + 16 + 5 + 18 + 5, MwTextWidth(window, "Password:"), 16, + MwNtext, "Password:", + NULL); + passentry = MwVaCreateWidget(MwEntryClass, "passentry", window, 95, 10 + 16 + 5 + 18 + 5 + 16 + 5, 265, 18, + MwNhideInput, 1, + NULL); + mainsep = MwVaCreateWidget(MwSeparatorClass, "mainsep", window, 95, 10 + 16 + 5 + 18 + 5 + 16 + 5 + 18, 265, 10, + MwNorientation, MwHORIZONTAL, + NULL); + sesslabel = MwVaCreateWidget(MwLabelClass, "sesslabel", window, 95, 10 + 16 + 5 + 18 + 5 + 16 + 5 + 18 + 10, MwTextWidth(window, "Session Type:"), 16, + MwNtext, "Session Type:", + NULL); + sesscombo = MwCreateWidget(MwComboBoxClass, "sesscombo", window, 95, 10 + 16 + 5 + 18 + 5 + 16 + 5 + 18 + 10 + 16 + 5, 265, 18); + fieldsep = MwVaCreateWidget(MwSeparatorClass, "fieldsep", window, 10, 10 + 137, 351, 10, + MwNorientation, MwHORIZONTAL, + NULL); + shutdown = MwVaCreateWidget(MwButtonClass, "shutdown", window, 10, 10 + 137 + 10, 80, 18, + MwNtext, "Shut Down", + NULL); + reboot = MwVaCreateWidget(MwButtonClass, "reboot", window, 10 + 80 + 5, 10 + 137 + 10, 70, 18, + MwNtext, "Reboot", + NULL); + ok = MwVaCreateWidget(MwButtonClass, "ok", window, 366 - 10 - 60, 10 + 137 + 10, 60, 18, + MwNtext, "OK", + NULL); + error = MwVaCreateWidget(MwLabelClass, "error", window, 10, 10 + 137 + 10 + 20, 366, 48, NULL); + if(p != NULL) MwVaApply(pic, MwNpixmap, p, NULL); + + MwAddUserHandler(ok, MwNactivateHandler, try_login, NULL); + MwAddUserHandler(passentry, MwNactivateHandler, try_login, NULL); + + MDEUsersList(add_user, usercombo); + + for(i = 0; i < shlen(sessions); i++) { + free(sessions[i].value); + } + shfree(sessions); + sh_new_strdup(sessions); + shdefault(sessions, NULL); + MDEDirectoryScan(DATADIR "/xsessions", add_session_x, sesscombo); + MDEDirectoryScan("/usr/share/xsessions", add_session_x, sesscombo); + MDEDirectoryScan(DATADIR "/wayland-sessions", add_session_wayland, sesscombo); + MDEDirectoryScan("/usr/share/wayland-sessions", add_session_wayland, sesscombo); + + pthread_mutex_unlock(&xmutex); + + while(1) { + int s = 0; + + pthread_mutex_lock(&xmutex); + while(MwPending(root)) { + if((s = MwStep(root)) != 0) break; + } + pthread_mutex_unlock(&xmutex); + if(s != 0) break; + + MwTimeSleep(30); + } + + pthread_join(xthread, &out); +} diff --git a/mdm/main.c b/mdm/main.c new file mode 100644 index 0000000..455ef3f --- /dev/null +++ b/mdm/main.c @@ -0,0 +1,128 @@ +#include "mdm.h" +#include +#include +#include +#include + +int is_launch_x = 1; +int is_daemon = 1; + +gid_t gid; +uid_t uid; +session_environment_t* env; + +static void set_env_standard(struct passwd* pwd); +static void set_env_xdg(struct passwd* pwd); + +int main(int argc, char** argv) { + int i, st, exit; + char* backup = NULL; + pid_t pid; + + for(i = 1; i < argc; i++) { + if(argv[i][0] == '-') { + if(strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--no-launch-x") == 0) { + is_launch_x = 0; + } else if(strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--no-daemon") == 0) { + is_daemon = 0; + } else { + fprintf(stderr, "%s: bad option: %s\n", argv[0], argv[i]); + return 1; + } + } + } + + if(getuid() != 0) { + fprintf(stderr, "MDM has to be ran as root\n"); + return 1; + } + + if(is_daemon && (pid = fork()) != 0) { + FILE* f = fopen("/var/run/mdm.pid", "w"); + fprintf(f, "%d\n", pid); + fclose(f); + return 0; + } + + if(access(CONFDIR, F_OK) != 0) mkdir(CONFDIR, 0755); + if(access(CONFDIR "/mdm", F_OK) != 0) mkdir(CONFDIR "/mdm", 0755); + if(access(CONFDIR "/mdm/mdmrc", F_OK) != 0) MDEFileCopy(DATADIR "/examples/mdm/mdmrc", CONFDIR "/mdm/mdmrc"); + + parse_config(); + + if(getenv("DISPLAY") != NULL) backup = MDEStringDuplicate(getenv("DISPLAY")); + do { + setenv("DISPLAY", backup == NULL ? "" : backup, 1); + if(is_launch_x && (st = launch_x()) != 0) return st; + + if((st = init_x()) != 0) return st; + + login_window(); + + if(env->run == NULL && env->try_run == NULL) continue; + + if((pid = fork()) == 0) { + struct passwd* pwd = getpwuid(uid); + + setgid(gid); + setuid(uid); + chdir(pwd->pw_dir); + + set_env_standard(pwd); + set_env_xdg(pwd); + + if((exit = system(env->try_run)) != 0) { + exit = system(env->run); + } else { + _exit(exit); + } + } else { + waitpid(pid, NULL, 0); + } + + if(is_launch_x) { + unsetenv("DISPLAY"); + kill_x(); + } + } while(is_launch_x); + + if(backup != NULL) free(backup); +} + +void set_env_standard(struct passwd* pwd) { + setenv("HOME", pwd->pw_dir, 1); + setenv("PWD", pwd->pw_dir, 1); + setenv("USER", pwd->pw_name, 1); + setenv("SHELL", pwd->pw_shell, 1); + setenv("LOGNAME", pwd->pw_shell, 1); +} +void set_env_xdg(struct passwd* pwd) { + // this directory isn't avaliable on bsd, but we can't check for all of them. + // so just assume it's only on linux, idek if that's true but who care +#if defined(__linux__) + char runtime_dir[255]; + snprintf(runtime_dir, 255, "/run/user/%d", pwd->pw_uid); + setenv("XDG_RUNTIME_DIR", runtime_dir, 1); +#endif + + switch(env->session_type) { + case MDM_SESSION_TYPE_X: + setenv("XDG_SESSION_TYPE", "x11", 1); + break; + case MDM_SESSION_TYPE_WAYLAND: + setenv("XDG_SESSION_TYPE", "wayland", 1); + break; + } + + if(env->desktop_names) { + if(strlen(env->desktop_names) > 1) { + setenv("XDG_SESSION_DESKTOP", strtok(env->desktop_names, ";"), 1); + } + setenv("XDG_CURRENT_DESKTOP", env->desktop_names, 1); + } + + setenv("XDG_SESSION_CLASS", "user", 1); + setenv("XDG_SESSION_ID", "1", 1); + setenv("XDG_SEAT", "seat0", 1); + // setenv("XDG_VTNR", "/dev/pts/7", 1); +} diff --git a/mdm/mdm.h b/mdm/mdm.h new file mode 100644 index 0000000..25375f7 --- /dev/null +++ b/mdm/mdm.h @@ -0,0 +1,62 @@ +#ifndef __MDM_H__ +#define __MDM_H__ + +#include "config.h" + +/* Milsko */ +#include +#include + +/* External */ +#include + +/* Standard */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* login.c */ +extern MwWidget root; +typedef enum session_type { + MDM_SESSION_TYPE_X, + MDM_SESSION_TYPE_WAYLAND, +} session_type_t; +typedef struct session_environment { + char* run; + char* try_run; + session_type_t session_type; + char* desktop_names; +} session_environment_t; + +void login_window(void); + +/* main.c */ +extern gid_t gid; +extern uid_t uid; +extern session_environment_t* env; + +/* xserver.c */ +extern pthread_t xthread; +extern pthread_mutex_t xmutex; +extern Display* xdisplay; + +int launch_x(void); +int x_width(void); +int x_height(void); +int init_x(void); +void loop_x(void); +void kill_x(void); + +/* config.c */ +extern char* config_picture; + +void parse_config(void); + +#endif diff --git a/mdm/mdm.png b/mdm/mdm.png new file mode 100644 index 0000000..a190f66 Binary files /dev/null and b/mdm/mdm.png differ diff --git a/mdm/mdmrc.in b/mdm/mdmrc.in new file mode 100644 index 0000000..dd1e3fe --- /dev/null +++ b/mdm/mdmrc.in @@ -0,0 +1,2 @@ +[MDM Config] +Picture=@CMAKE_INSTALL_FULL_DATAROOTDIR@/mdm/mdm.png diff --git a/mdm/pam b/mdm/pam new file mode 100644 index 0000000..e21bb83 --- /dev/null +++ b/mdm/pam @@ -0,0 +1,5 @@ +auth required pam_unix.so +auth required pam_nologin.so +account required pam_unix.so +password required pam_unix.so +session required pam_unix.so diff --git a/mdm/stb_ds.c b/mdm/stb_ds.c new file mode 100644 index 0000000..689615c --- /dev/null +++ b/mdm/stb_ds.c @@ -0,0 +1,2 @@ +#define STB_DS_IMPLEMENTATION +#include diff --git a/mdm/xserver.c b/mdm/xserver.c new file mode 100644 index 0000000..e1bb954 --- /dev/null +++ b/mdm/xserver.c @@ -0,0 +1,163 @@ +#include +#define _MILSKO +#define USE_X11 +#include "mdm.h" + +#include "stb_ds.h" + +static pid_t xserver; +static int got_usr1; + +Display* xdisplay; +pthread_t xthread; +pthread_mutex_t xmutex; + +static void catch_usr1(int sig) { + (void)sig; + + got_usr1 = 1; +} + +int launch_x(void) { + got_usr1 = 0; + + signal(SIGUSR1, catch_usr1); + + if((xserver = fork()) == 0) { + signal(SIGUSR1, SIG_IGN); + execlp("X", "X", ":0", NULL); + _exit(-1); + } else { + while(!got_usr1) { + if(waitpid(xserver, NULL, WNOHANG) != 0) { + fprintf(stderr, "server died. what happened?\n"); + return 1; + } + usleep(1000); + } + } + + setenv("DISPLAY", ":0", 1); + + return 0; +} + +static int wm_detected = 0; +static int wm_detect(Display* disp, XErrorEvent* ev) { + (void)disp; + (void)ev; + + wm_detected = 1; +} + +static void* x11_thread_routine(void* arg) { + loop_x(); + XCloseDisplay(xdisplay); +} + +int init_x(void) { + XColor bg; + void* old; + + xdisplay = XOpenDisplay(NULL); + + bg.red = 0x1c * 256; + bg.green = 0x45 * 256; + bg.blue = 0x73 * 256; + XAllocColor(xdisplay, DefaultColormap(xdisplay, DefaultScreen(xdisplay)), &bg); + + XSetWindowBackground(xdisplay, DefaultRootWindow(xdisplay), bg.pixel); + XClearWindow(xdisplay, DefaultRootWindow(xdisplay)); + XFlush(xdisplay); + + old = XSetErrorHandler(wm_detect); + XSelectInput(xdisplay, DefaultRootWindow(xdisplay), SubstructureRedirectMask); + XSync(xdisplay, False); + XSetErrorHandler(old); + + if(wm_detected) { + fprintf(stderr, "WM or something seem to be running\n"); + return 1; + } + + pthread_mutex_init(&xmutex, NULL); + + pthread_create(&xthread, NULL, x11_thread_routine, NULL); + + return 0; +} + +int x_width(void) { + return DisplayWidth(xdisplay, DefaultScreen(xdisplay)); +} + +int x_height(void) { + return DisplayHeight(xdisplay, DefaultScreen(xdisplay)); +} + +typedef struct window { + MwWidget frame; + Window client; +} window_t; + +void loop_x(void) { + XEvent ev; + window_t* frames = NULL; + + while(1) { + XNextEvent(xdisplay, &ev); + if(ev.type == MapRequest) { + XWindowAttributes xwa; + window_t w; + int i; + + pthread_mutex_lock(&xmutex); + for(i = 0; i < arrlen(frames); i++) { + if(frames[i].frame->lowlevel->x11.window == ev.xmaprequest.window) { + XReparentWindow(xdisplay, frames[i].client, frames[i].frame->lowlevel->x11.window, MwDefaultBorderWidth(root), MwDefaultBorderWidth(root)); + XMapWindow(xdisplay, ev.xmaprequest.window); + XMapWindow(xdisplay, frames[i].client); + break; + } + } + if(i != arrlen(frames)) { + pthread_mutex_unlock(&xmutex); + continue; + } + + XGetWindowAttributes(xdisplay, ev.xmaprequest.window, &xwa); + + w.client = ev.xmaprequest.window; + w.frame = MwVaCreateWidget(MwWindowClass, "frame", root, xwa.x - MwDefaultBorderWidth(root), xwa.y - MwDefaultBorderWidth(root), xwa.width + MwDefaultBorderWidth(root) * 2, xwa.height + MwDefaultBorderWidth(root) * 2, + MwNhasBorder, 1, + MwNinverted, 0, + NULL); + + XSelectInput(xdisplay, w.client, StructureNotifyMask); + + arrput(frames, w); + pthread_mutex_unlock(&xmutex); + } else if(ev.type == UnmapNotify) { + int i; + int c = 0; + + pthread_mutex_lock(&xmutex); + for(i = 0; i < arrlen(frames); i++) { + if(frames[i].client == ev.xunmap.window) { + MwDestroyWidget(frames[i].frame); + arrdel(frames, i); + c = 1; + break; + } + } + pthread_mutex_unlock(&xmutex); + + if(c) break; + } + } +} + +void kill_x(void) { + kill(xserver, SIGINT); + waitpid(xserver, NULL, 0); +} diff --git a/milkwm/CMakeLists.txt b/milkwm/CMakeLists.txt new file mode 100644 index 0000000..1c6db46 --- /dev/null +++ b/milkwm/CMakeLists.txt @@ -0,0 +1,2 @@ +include(MDEBase) +setup_project(milkwm ${CMAKE_INSTALL_BINDIR}) diff --git a/milkwm/main.c b/milkwm/main.c new file mode 100644 index 0000000..237c8ce --- /dev/null +++ b/milkwm/main.c @@ -0,0 +1 @@ +int main() {} diff --git a/mpanel/CMakeLists.txt b/mpanel/CMakeLists.txt new file mode 100644 index 0000000..8e1c41b --- /dev/null +++ b/mpanel/CMakeLists.txt @@ -0,0 +1,10 @@ +include(MDEBase) +setup_project(mpanel ${CMAKE_INSTALL_BINDIR}) + +if(${CMAKE_SYSTEM_NAME} STREQUAL Linux) + target_link_libraries( + mpanel + PRIVATE + dl + ) +endif() diff --git a/mpanel/main.c b/mpanel/main.c new file mode 100644 index 0000000..237c8ce --- /dev/null +++ b/mpanel/main.c @@ -0,0 +1 @@ +int main() {}