diff --git a/CMakeLists.txt b/CMakeLists.txt index becc390..ac3e474 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,273 +1,171 @@ # -*- mode: cmake -*- -#Cmakelists.txt -# Minimum version of CMake -cmake_minimum_required(VERSION 2.6) + +# Minimal version of CMake +cmake_minimum_required(VERSION 2.6.0) +cmake_minimum_required(VERSION 2.6.0 FATAL_ERROR) if(COMMAND cmake_policy) - cmake_policy(VERSION 2.6) -endif() + cmake_policy(SET CMP0003 NEW) +endif(COMMAND cmake_policy) -CONFIGURE_FILE( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY) +# set some default options +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH} ) +set(CMAKE_COLOR_MAKEFILE ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_BUILD_TYPE_SHARED_LIBS ON) +set(CMAKE_C_FLAGS $ENV{CFLAGS}) +set(CMAKE_CXX_FLAGS $ENV{CXXFLAGS}) +set(CMAKE_LINK_FLAGS $ENV{LDFLAGS}) -ADD_CUSTOM_TARGET(uninstall - "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") +# include macros +include(MacroConfigureFile) -# Source and build dirs -set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) +# uninstall +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY ) +add_custom_target( uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" ) # Project name - wmfs set(PROJECT_NAME wmfs) +set(VERSION "WMFS-201008") project(${PROJECT_NAME} C) -# Definition of the wmfs source -set(wmfs_src - src/parse/parse.c - src/barwin.c - src/client.c - src/config.c - src/ - src/draw.c - src/event.c - src/ewmh.c - src/frame.c - src/getinfo.c - src/infobar.c - src/init.c - src/launcher.c - src/layout.c - src/menu.c - src/mouse.c - src/screen.c - src/status.c - src/systray.c - src/tag.c - src/util.c - src/viwmfs.c - src/wmfs.c) +find_package(PkgConfig REQUIRED) +# required packages +pkg_check_modules(FREETYPE REQUIRED freetype2) +pkg_check_modules(X11 REQUIRED x11) +pkg_check_modules(XFT REQUIRED xft) -# Set the executable from the wmfs_src -add_executable(wmfs ${wmfs_src}) - -# Set the version -set(VERSION "WMFS-201006") - -# FLAGS -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -ansi") - -# Linker FLAGS - -set(DEFAULT_LDFLAGS "-L /usr/local/lib -lpthread") - -if(CMAKE_SYSTEM_NAME MATCHES NetBSD) - message("-- NetBSD system found - Using /usr/pkg/lib for linker") - set(LDFLAGS "${DEFAULT_LDFLAGS} -L /usr/pkg/lib") -else(CMAKE_SYSTEM_NAME MATCHES NetBSD) - set(LDFLAGS ${DEFAULT_LDFLAGS}) -endif(CMAKE_SYSTEM_NAME MATCHES NetBSD) - -set_target_properties(wmfs PROPERTIES LINK_FLAGS ${LDFLAGS}) -# Includes dir for libs in build_dir include_directories( - ${BUILD_DIR}/src - ) + ${FREETYPE_INCLUDE_DIRS} + ${X11_INCLUDE_DIRS} + ${XFT_INCLUDE_DIRS} +) -# Package find -find_package(Freetype) -if(FREETYPE_FOUND) - include_directories(${FREETYPE_INCLUDE_DIRS}) -else (FREETYPE_FOUND) - message(FATAL_ERROR "Could not find Freetype") -endif (FREETYPE_FOUND) +set(CMAKE_LINK_LIBRARIES + ${FREETYPE_LIBRARIES} + ${X11_LIBRARIES} + ${XFT_LIBRARIES} + -lpthread +) -find_package(X11) -if(X11_FOUND) - include_directories(${X11_INCLUDE_DIR}) -else (X11_FOUND) - message(FATAL_ERROR "Could not find X11") -endif (X11_FOUND) +# options +option(BUILD_DOC "Generate documentation using doxygen" OFF) +option(WITH_XINERAMA "Build with Xinerama support" ON) +option(WITH_XRANDR "Build with Xrandr support" ON) +option(WITH_IMLIB2 "Build with Imlib2 support" ON) -# Link Libraries -set(LIBRARIES_TO_LINK - ${FREETYPE_LIBRARIES} - ${X11_LIBRARIES} - Xft) +# optional finders +if(WITH_XINERAMA) + add_definitions(-DHAVE_XINERAMA) + pkg_check_modules(XINERAMA REQUIRED xinerama) + include_directories(${XINERAMA_INCLUDE_DIRS}) + set(CMAKE_LINK_LIBRARIES ${XINERAMA_LIBRARIES} ${CMAKE_LINK_LIBRARIES}) +else(WITH_XINERAMA) + message(STATUS "Not building with Xinerama support") +endif(WITH_XINERAMA) -# Includes -include(FindDoxygen) -include(FindPkgConfig) +if(WITH_XRANDR) + add_definitions(-DHAVE_XRANDR) + pkg_check_modules(XRANDR REQUIRED xrandr) + include_directories(${XRANDR_INCLUDE_DIRS}) + set(CMAKE_LINK_LIBRARIES ${XRANDR_LIBRARIES} ${CMAKE_LINK_LIBRARIES}) +else(WITH_XRANDR) + message(STATUS "Not building with Xrandr support") +endif(WITH_XRANDR) -# Use pkgconfig to get required libraries -pkg_check_modules(WMFS_REQUIRED REQUIRED - x11 - freetype2 - xft) +if(WITH_IMLIB2) + add_definitions(-DHAVE_IMLIB) + pkg_check_modules(IMLIB2 REQUIRED imlib2) + include_directories(${IMLIB2_INCLUDE_DIRS}) + set(CMAKE_LINK_LIBRARIES ${IMLIB2_LIBRARIES} ${CMAKE_LINK_LIBRARIES}) +else(WITH_IMLIB2) + message(STATUS "Not building with Imlib2 support") +endif(WITH_IMLIB2) -# Optional dependencies check +set(CMAKE_SYSCONFDIR ${CMAKE_INSTALL_PREFIX}/etc/ CACHE PATH "Config directory") +set(CMAKE_XDGCONFDIR ${CMAKE_SYSCONFDIR}/xdg/ CACHE PATH "XDG config directory") +set(CMAKE_DATADIR ${CMAKE_INSTALL_PREFIX}/share/ CACHE PATH "Data directory") +set(CMAKE_DOCDIR ${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}-${VERSION} CACHE PATH "Data directory") -# Check for xinerama -pkg_check_modules(HAVE_XINERAMA xinerama) -if(HAVE_XINERAMA_FOUND) - set(WMFS_HAVE_XINERAMA "#define HAVE_XINERAMA") - set(LIBRARIES_TO_LINK ${LIBRARIES_TO_LINK} Xinerama) -else() - set(WMFS_HAVE_XINERAMA "") -endif() +# set default terminal for user +set(WMFS_TERM xterm) +# get the user compiling this thing +SET(USERNAME $ENV{USER}) -# Check for xrandr -pkg_check_modules(HAVE_XRANDR xrandr) -if(HAVE_XRANDR_FOUND) - set(WMFS_HAVE_XRANDR "#define HAVE_XRANDR") - set(LIBRARIES_TO_LINK ${LIBRARIES_TO_LINK} Xrandr) -else() - set(WMFS_HAVE_XRANDR "") -endif() - -# Check for Imlib -pkg_check_modules(HAVE_IMLIB imlib2) -if(HAVE_IMLIB_FOUND) - set(WMFS_HAVE_IMLIB "#define HAVE_IMLIB") - set(LIBRARIES_TO_LINK ${LIBRARIES_TO_LINK} Imlib2) -else() - set(WMFS_HAVE_IMLIB "") -endif() - -target_link_libraries(wmfs ${LIBRARIES_TO_LINK}) - -# Messages -message("Project version: ${VERSION}") -message("Using these CFLAGS: ${CMAKE_C_FLAGS}") -message("Using these LDFLAGS: ${LDFLAGS}") -message("Linking with theses libraries : ${LIBRARIES_TO_LINK}") - -# Generating man page -find_program(GZIP_EXECUTABLE gzip) -if(NOT GZIP_EXECUTABLE) - message(STATUS "Looking for gzip -- not found") - message(STATUS "Could not generating man page") -else() - message(STATUS "Looking for gzip -- ${GZIP_EXECUTABLE}") - message(STATUS "Generating man page") - set(WMFS_MAN1_FILES ${BUILD_DIR}/wmfs.1.gz) - execute_process( - COMMAND ${GZIP_EXECUTABLE} -c wmfs.1 - WORKING_DIRECTORY ${SOURCE_DIR} - OUTPUT_FILE ${WMFS_MAN1_FILES}) -endif() - -# Generating CHANGELOG -find_program(GIT_EXECUTABLE git) -if(EXISTS ${SOURCE_DIR}/.git/HEAD AND GIT_EXECUTABLE) - message(STATUS "Looking for git -- ${GIT_EXECUTABLE}") - message(STATUS "Git dir -- Generating changelog...") - set(PROJECT_CHANGELOG ${SOURCE_DIR}/changelog) - execute_process( - COMMAND ${GIT_EXECUTABLE} log - WORKING_DIRECTORY ${SOURCE_DIR} - OUTPUT_FILE ${PROJECT_CHANGELOG}) -else() - message(STATUS "Looking for git -- not found") - message(STATUS "Could not generating changelog") -endif() - -# sets -# {{{ Install path and configuration variables -if(DEFINED PREFIX) - set(PREFIX ${PREFIX} CACHE PATH "install prefix") - set(CMAKE_INSTALL_PREFIX ${PREFIX}) -else() - set(PREFIX ${CMAKE_INSTALL_PREFIX} CACHE PATH "install prefix") -endif() - -#If a sysconfdir is specified, use it instead -#of the default configuration dir. -if(DEFINED SYSCONFDIR) - set(SYSCONFDIR ${SYSCONFDIR} CACHE PATH "config directory") -else() - set(SYSCONFDIR /etc CACHE PATH "config directory") -endif() - -#If an XDG Config Dir is specificed, use it instead -#of the default XDG configuration dir. -if(DEFINED XDG_CONFIG_DIR) - set(XDG_CONFIG_DIR ${XDG_CONFIG_SYS} CACHE PATH "xdg config directory") -else() - set(XDG_CONFIG_DIR ${SYSCONFDIR}/xdg CACHE PATH "xdg config directory") -endif() - -# setting WMFS_XSESSION_PATH -if(DEFINED WMFS_XSESSION_PATH) - set(WMFS_XSESSION_PATH ${WMFS_XSESSION_PATH} CACHE PATH "wmfs xsessions directory") -else() - set(WMFS_XSESSION_PATH ${PREFIX}/share/xsessions CACHE PATH "wmfs xsessions directory") -endif() - -if(DEFINED WMFS_MAN_PATH) - set(WMFS_MAN_PATH ${WMFS_MAN_PATH} CACHE PATH "wmfs manpage directory") -else() - set(WMFS_MAN_PATH ${PREFIX}/share/man CACHE PATH "wmfs manpage directory") -endif() - -if(DOXYGEN_EXECUTABLE) - add_custom_target(doc - COMMAND ${DOXYGEN_EXECUTABLE} ${SOURCE_DIR}/wmfs.doxygen - WORKING_DIRECTORY ${BUILD_DIR}) -endif() - -find_program(URXVT_EXECUTABLE urxvt) -if(URXVT_EXECUTABLE) - set(WMFS_TERM urxvt) -else() - set(WMFS_TERM xterm) -endif() - -# Remplace strings in configs -set(WMFS_VERSION ${VERSION}) -set(WMFS_COMPILE_MACHINE ${CMAKE_SYSTEM_PROCESSOR}) -set(WMFS_COMPILE_BY $ENV{USER}) -set(WMFS_COMPILE_FLAGS ${CMAKE_C_FLAGS}) -set(WMFS_LINKED_LIBS ${LIBRARIES_TO_LINK}) -set(WMFS_SYSCONFDIR ${XDG_CONFIG_DIR}/${PROJECT_NAME}) -set(WMFS_SOURCE_DIR ${SOURCE_DIR}) - -# Configure files -set(wmfs_configure_files +# configure files +set(CONFIGURE_FILES src/config.h.in wmfs.doxygen.in wmfsrc.in) +foreach(file ${CONFIGURE_FILES}) + ConfigureFile(${file}) +endforeach(file) -macro(a_configure_file file) - string(REGEX REPLACE ".in\$" "" outfile ${file}) - message(STATUS "Configuring ${outfile}") - configure_file(${SOURCE_DIR}/${file} - ${SOURCE_DIR}/${outfile} - ESCAPE_QUOTE - @ONLY) -endmacro() +# add the source directory +add_subdirectory(src) -foreach(file ${wmfs_configure_files}) - a_configure_file(${file}) -endforeach() +# documentation +if(BUILD_DOC) + find_package(Doxygen REQUIRED) + if(DOXYGEN_FOUND) + add_custom_target(doc + COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/wmfs.doxygen + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + else(DOXYGEN_FOUND) + MESSAGE(FATAL_ERROR "Doxygen executable not found") + endif(DOXYGEN_FOUND) +endif(BUILD_DOC) -set(PROJECT_DATA_PATH share/${PROJECT_NAME}) -set(PROJECT_TODO ${SOURCE_DIR}/TODO) -set(PROJECT_README ${SOURCE_DIR}/README) -set(PROJECT_DEFAULT_CONF ${SOURCE_DIR}/wmfsrc) +# Generating ChangeLog only for live version, for release we generate it to tarball +if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/ChangeLog) + find_program(GIT_EXECUTABLE git) + if(EXISTS ${CMAKE_SOURCE_DIR}/.git/HEAD AND GIT_EXECUTABLE) + message(STATUS "<<< Generating ChangeLog... >>>") + execute_process( + COMMAND ${GIT_EXECUTABLE} log + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/ChangeLog) + else(EXISTS ${CMAKE_SOURCE_DIR}/.git/HEAD AND GIT_EXECUTABLE) + message(STATUS "No ChangeLog present and git not found") + message(STATUS "<<< Will not generate ChangeLog >>>") + endif(EXISTS ${CMAKE_SOURCE_DIR}/.git/HEAD AND GIT_EXECUTABLE) +endif(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/ChangeLog) -# installs -install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) +# manpage (compression handled by system not by us) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/wmfs.1 DESTINATION ${CMAKE_DATADIR}/man/man1) -if(WMFS_MAN1_FILES) - install(FILES ${WMFS_MAN1_FILES} DESTINATION ${WMFS_MAN_PATH}/man1) -endif() +# install docs +# changelog || can have 2 locations based on whether we generate it or not +if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/ChangeLog) + set(WMFS_DOCS ${CMAKE_CURRENT_BINARY_DIR}/ChangeLog ${WMFS_DOCS}) +endif(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/ChangeLog) +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/ChangeLog) + set(WMFS_DOCS ${CMAKE_CURRENT_SOURCE_DIR}/ChangeLog ${WMFS_DOCS}) +endif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/ChangeLog) +set(WMFS_DOCS + ${CMAKE_CURRENT_SOURCE_DIR}/TODO + ${CMAKE_CURRENT_SOURCE_DIR}/README + ${WMFS_DOCS} +) +install(FILES ${WMFS_DOCS} DESTINATION ${CMAKE_DOCDIR}) -if(PROJECT_CHANGELOG) - install(FILES ${PROJECT_CHANGELOG} DESTINATION ${PROJECT_DATA_PATH}) -endif() +# config file +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/wmfsrc DESTINATION ${CMAKE_XDGCONFDIR}/${PROJECT_NAME}) -install(FILES ${PROJECT_TODO} ${PROJECT_README} - ${PROJECT_DEFAULT_CONF} DESTINATION ${PROJECT_DATA_PATH}) -install(FILES ${PROJECT_DEFAULT_CONF} DESTINATION ${WMFS_SYSCONFDIR}) -install(FILES "wmfs.desktop" DESTINATION ${WMFS_XSESSION_PATH}) +# xsession +install(FILES "wmfs.desktop" DESTINATION ${CMAKE_DATADIR}/xsessions) + +# Status messages +message(STATUS "<<< ${PROJECT_NAME} ${VERSION} configuration >>> + Build type ${CMAKE_BUILD_TYPE} + Install path ${CMAKE_INSTALL_PREFIX} + Compiler flags: + C ${CMAKE_C_FLAGS} + C++ ${CMAKE_CXX_FLAGS} + Linker flags: + Executable ${CMAKE_EXE_LINKER_FLAGS} + Module ${CMAKE_MODULE_LINKER_FLAGS} + Shared ${CMAKE_SHARED_LINKER_FLAGS}\n") diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100755 index 0000000..cd4c9d8 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,21 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") +FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling: \"$ENV{DESTDIR}${file}\"") + IF(EXISTS "$ENV{DESTDIR}${file}") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing: \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + ENDIF(EXISTS "$ENV{DESTDIR}${file}") +ENDFOREACH(file) diff --git a/cmake/modules/MacroConfigureFile.cmake b/cmake/modules/MacroConfigureFile.cmake new file mode 100644 index 0000000..b2ae132 --- /dev/null +++ b/cmake/modules/MacroConfigureFile.cmake @@ -0,0 +1,8 @@ +macro(ConfigureFile file) + string(REGEX REPLACE ".in\$" "" outfile ${file}) + message(STATUS "<<< Configuring ${outfile} >>>") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${file} + ${CMAKE_CURRENT_BINARY_DIR}/${outfile} + ESCAPE_QUOTE + @ONLY) +endmacro(ConfigureFile) diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in deleted file mode 100644 index df95fb9..0000000 --- a/cmake_uninstall.cmake.in +++ /dev/null @@ -1,21 +0,0 @@ -IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") - MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") -ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") - -FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) -STRING(REGEX REPLACE "\n" ";" files "${files}") -FOREACH(file ${files}) - MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") - IF(EXISTS "$ENV{DESTDIR}${file}") - EXEC_PROGRAM( - "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" - OUTPUT_VARIABLE rm_out - RETURN_VALUE rm_retval - ) - IF(NOT "${rm_retval}" STREQUAL 0) - MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") - ENDIF(NOT "${rm_retval}" STREQUAL 0) - ELSE(EXISTS "$ENV{DESTDIR}${file}") - MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") - ENDIF(EXISTS "$ENV{DESTDIR}${file}") -ENDFOREACH(file) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..24a540e --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,35 @@ +# -*- mode: cmake -*- + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/parse) + +SET(WMFS_SOURCES + parse/parse.c + parse/api.c + barwin.c + client.c + config.c + draw.c + event.c + ewmh.c + frame.c + getinfo.c + infobar.c + init.c + launcher.c + layout.c + menu.c + mouse.c + screen.c + status.c + systray.c + tag.c + util.c + viwmfs.c + wmfs.c +) + +add_executable(${PROJECT_NAME} ${WMFS_SOURCES}) +target_link_libraries(${PROJECT_NAME} ${CMAKE_LINK_LIBRARIES}) +INSTALL ( TARGETS ${PROJECT_NAME} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} ) diff --git a/src/barwin.c b/src/barwin.c index 2b0fad9..7cdd641 100644 --- a/src/barwin.c +++ b/src/barwin.c @@ -123,6 +123,26 @@ barwin_draw_text(BarWindow *bw, int x, int y, char *text) return; } +/** Draw text in a Barwindow +*/ +void +barwin_draw_image_ofset_text(BarWindow *bw, int x, int y, char *text, int x_image_ofset, int y_image_ofset) +{ + if(!text) + return; + + /* Background color of the text if there is stipple */ + if(bw->stipple) + draw_rectangle(bw->dr, x - 4, 0, textw(text) + 8, bw->geo.height, bw->bg); + + /* Draw text */ + draw_image_ofset_text(bw->dr, x, y, bw->fg, 0, text, x_image_ofset, y_image_ofset); + + barwin_refresh(bw); + + return; +} + /** Delete a BarWindow * \param bw BarWindow pointer */ diff --git a/src/client.c b/src/client.c index b251c8a..904b008 100644 --- a/src/client.c +++ b/src/client.c @@ -93,6 +93,8 @@ client_get_next(void) { Client *c = NULL; + screen_get_sel(); + if(!sel || ishide(sel, selscreen)) return NULL; @@ -112,6 +114,8 @@ client_get_prev(void) { Client *c = NULL, *d; + screen_get_sel(); + if(!sel || ishide(sel, selscreen)) return NULL; @@ -987,7 +991,11 @@ void client_set_rules(Client *c) { XClassHint xch = { 0 }; - int i, j, k; + int i, j, k, f; + Atom rf; + ulong n, il; + uchar *data = NULL; + char wwrole[256] = { 0 }; if(conf.ignore_next_client_rules) { @@ -995,8 +1003,19 @@ client_set_rules(Client *c) return; } + /* Get WM_CLASS */ XGetClassHint(dpy, c->win, &xch); + /* Get WM_WINDOW_ROLE */ + if(XGetWindowProperty(dpy, c->win, ATOM("WM_WINDOW_ROLE"), 0L, 0x7FFFFFFFL, False, + XA_STRING, &rf, &f, &n, &il, &data) == Success && data) + { + strcpy(wwrole, (char*)data); + XFree(data); + } + + /* Following features is *DEPRECATED*, will be removed in some revision. {{{ */ + /* Auto free */ if(conf.client.autofree && ((xch.res_name && strstr(conf.client.autofree, xch.res_name)) || (xch.res_class && strstr(conf.client.autofree, xch.res_class)))) @@ -1030,6 +1049,42 @@ client_set_rules(Client *c) tags[c->screen][c->tag].layout.func(c->screen); } + /* }}} */ + + /* Apply Rule if class || instance || role match */ + for(i = 0; i < conf.nrule; ++i) + { + if((xch.res_class && conf.rule[i].class && !strcmp(xch.res_class, conf.rule[i].class)) + || (xch.res_name && conf.rule[i].instance && !strcmp(xch.res_name, conf.rule[i].instance))) + { + if((strlen(wwrole) && conf.rule[i].role && !strcmp(wwrole, conf.rule[i].role)) || (!strlen(wwrole) || !conf.rule[i].role)) + { + if(conf.rule[i].screen != -1) + c->screen = conf.rule[i].screen; + + if(conf.rule[i].tag != -1) + c->tag = conf.rule[i].tag; + + if(conf.rule[i].free) + c->flags |= FreeFlag; + + if(conf.rule[i].max) + { + client_maximize(c); + c->flags |= MaxFlag; + } + + if(c->tag != seltag[selscreen]) + { + tags[c->screen][c->tag].request_update = True; + client_focus(NULL); + } + + tags[c->screen][c->tag].layout.func(c->screen); + } + } + } + return; } @@ -1342,13 +1397,17 @@ uicb_clientlist(uicb_t cmd) { int i, d, u, x, y; int n = 0; + Bool all = False; Window w; Client *c = NULL; screen_get_sel(); + if(cmd && !strcmp(cmd, "all")) + all = True; + for(c = clients; c; c = c->next) - if(!ishide(c, selscreen)) + if(!ishide(c, selscreen) || all) ++n; if(n > 0) @@ -1363,8 +1422,10 @@ uicb_clientlist(uicb_t cmd) conf.colors.bar, conf.colors.text); + clientlist.align = MA_Left; + for(i = 0, c = clients; c; c = c->next) - if(!ishide(c, selscreen)) + if(!ishide(c, selscreen) || all) { sprintf(clist_index[i].key, "%d", i); clist_index[i].client = c; @@ -1401,6 +1462,12 @@ uicb_client_select(uicb_t cmd) for(i = 0; i < MAXCLIST && clist_index[i].client; ++i) if(!strcmp(cmd, clist_index[i].key)) { + if(clist_index[i].client->screen != selscreen) + screen_set_sel(clist_index[i].client->screen); + + if(clist_index[i].client->tag != seltag[clist_index[i].client->screen]) + tag_set(clist_index[i].client->tag); + client_focus(clist_index[i].client); client_raise(clist_index[i].client); diff --git a/src/config.c b/src/config.c index 89626f6..6739f77 100644 --- a/src/config.c +++ b/src/config.c @@ -181,7 +181,8 @@ void conf_bar_section(void) { struct conf_sec *bar, **mouse, *selbar, *systray; - char *barbg, sc = screen_count(); + char *barbg; + int sc = screen_count(); bar = fetch_section_first(NULL, "bar"); @@ -219,7 +220,7 @@ conf_bar_section(void) conf.selbar.bg = getcolor(fetch_opt_first(selbar, barbg, "bg").str); conf.selbar.fg = fetch_opt_first(selbar, conf.colors.text, "fg").str; - conf.selbar.maxlenght = fetch_opt_first(selbar, "-1", "max_lenght").num; + conf.selbar.maxlength = fetch_opt_first(selbar, "-1", "max_length").num; mouse = fetch_section(selbar, "mouse"); @@ -230,7 +231,6 @@ conf_bar_section(void) } free(mouse); - free(barbg); return; } @@ -393,6 +393,7 @@ conf_layout_section(void) layouts = fetch_section_first(NULL, "layouts"); + conf.layout_button_width = fetch_opt_first(layouts, "O", "layout_button_width").num; conf.border.layout = fetch_opt_first(layouts, "false", "border").bool; conf.colors.layout_fg = fetch_opt_first(layouts, "#ffffff", "fg").str; conf.colors.layout_bg = getcolor((fetch_opt_first(layouts, "#000000", "bg").str)); @@ -605,6 +606,36 @@ conf_tag_section(void) return; } +void +conf_rule_section(void) +{ + int i; + struct conf_sec *rules, **rule; + + rules = fetch_section_first(NULL, "rules"); + + rule = fetch_section(rules, "rule"); + + CHECK((conf.nrule = fetch_section_count(rule))); + + conf.rule = emalloc(conf.nrule, sizeof(Rule)); + + for(i = 0; i < conf.nrule; ++i) + { + conf.rule[i].class = fetch_opt_first(rule[i], "", "class").str; + conf.rule[i].instance = fetch_opt_first(rule[i], "", "instance").str; + conf.rule[i].role = fetch_opt_first(rule[i], "", "role").str; + conf.rule[i].screen = fetch_opt_first(rule[i], "-1", "screen").num; + conf.rule[i].tag = fetch_opt_first(rule[i], "-1", "tag").num; + conf.rule[i].free = fetch_opt_first(rule[i], "false", "free").bool; + conf.rule[i].max = fetch_opt_first(rule[i], "false", "max").bool; + } + + free(rule); + + return; +} + void conf_menu_section(void) { @@ -618,7 +649,7 @@ conf_menu_section(void) CHECK((conf.nmenu = fetch_section_count(set_menu))); - conf.menu = calloc(conf.nmenu, sizeof(Menu)); + conf.menu = emalloc(conf.nmenu, sizeof(Menu)); for(i = 0; i < conf.nmenu; ++i) { @@ -756,6 +787,7 @@ init_conf(void) conf_client_section(); conf_layout_section(); conf_tag_section(); + conf_rule_section(); conf_menu_section(); conf_launcher_section(); conf_keybind_section(); diff --git a/src/config.h.in b/src/config.h.in index e409351..77d88bd 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -35,16 +35,11 @@ #include "wmfs.h" -#define WMFS_VERSION "@WMFS_VERSION@" -#define WMFS_COMPILE_MACHINE "@WMFS_COMPILE_MACHINE@" -#define WMFS_COMPILE_BY "@WMFS_COMPILE_BY@" -#define WMFS_COMPILE_FLAGS "@WMFS_COMPILE_FLAGS@" -#define WMFS_LINKED_LIBS "@WMFS_LINKED_LIBS@" -#define XDG_CONFIG_DIR "@XDG_CONFIG_DIR@" - -/* Optional dependencies */ -@WMFS_HAVE_XINERAMA@ -@WMFS_HAVE_XRANDR@ -@WMFS_HAVE_IMLIB@ +#define WMFS_VERSION "@VERSION@" +#define WMFS_COMPILE_MACHINE "@CMAKE_SYSTEM_PROCESSOR@" +#define WMFS_COMPILE_BY "@USERNAME@" +#define WMFS_COMPILE_FLAGS "@CMAKE_C_FLAGS@" +#define WMFS_LINKED_LIBS "@CMAKE_LINK_LIBRARIES@" +#define XDG_CONFIG_DIR "@CMAKE_XDGCONFDIR@" #endif /* CONFIG_H */ diff --git a/src/draw.c b/src/draw.c index 872a278..9ee9680 100644 --- a/src/draw.c +++ b/src/draw.c @@ -32,6 +32,12 @@ #include "wmfs.h" +void +draw_text(Drawable d, int x, int y, char* fg, int pad, char *str) +{ + draw_image_ofset_text(d, x, y, fg, pad, str, 0, 0); +} + /** Draw a string in a Drawable * \param d Drawable * \param x X position @@ -41,7 +47,7 @@ * \param str String that will be draw */ void -draw_text(Drawable d, int x, int y, char* fg, int pad, char *str) +draw_image_ofset_text(Drawable d, int x, int y, char* fg, int pad, char *str, int x_image_ofset, int y_image_ofset) { XftColor xftcolor; XftDraw *xftd; @@ -65,7 +71,7 @@ draw_text(Drawable d, int x, int y, char* fg, int pad, char *str) sw = systray_get_width(); for(i = 0; i < ni; ++i) - draw_image(d, im[i].x - sw, im[i].y, im[i].w, im[i].h, im[i].name); + draw_image(d, x_image_ofset + im[i].x - sw, y_image_ofset + im[i].y, im[i].w, im[i].h, im[i].name); } #endif /* HAVE_IMLIB */ diff --git a/src/event.c b/src/event.c index 6429e96..d7da88e 100644 --- a/src/event.c +++ b/src/event.c @@ -65,7 +65,10 @@ buttonpress(XButtonEvent *ev) for(i = 0; i < conf.titlebar.button[n].nmouse; ++i) if(ev->button == conf.titlebar.button[n].mouse[i].button) if(conf.titlebar.button[n].mouse[i].func) + { + client_focus(c); conf.titlebar.button[n].mouse[i].func(conf.titlebar.button[n].mouse[i].cmd); + } /* Frame Resize Area */ if((c = client_gb_resize(ev->window))) diff --git a/src/infobar.c b/src/infobar.c index 8cd45d5..eeda99d 100644 --- a/src/infobar.c +++ b/src/infobar.c @@ -95,7 +95,7 @@ infobar_init(void) /* Create layout switch barwindow */ infobar[sc].layout_button = barwin_create(infobar[sc].bar->win, ((conf.layout_placement) ? 0 : (j + PAD / 2)), 0, - textw(tags[sc][seltag[sc]].layout.symbol) + PAD, + ((conf.layout_button_width > 0) ? conf.layout_button_width : (textw(tags[sc][seltag[sc]].layout.symbol) + PAD)), infobar[sc].geo.height, conf.colors.layout_bg, conf.colors.layout_fg, False, False, conf.border.layout); @@ -159,7 +159,7 @@ infobar_draw_layout(int sc) if(!conf.layout_placement) barwin_move(infobar[sc].layout_button, infobar[sc].tags_board->geo.width + PAD / 2, 0); - barwin_resize(infobar[sc].layout_button, textw(tags[sc][seltag[sc]].layout.symbol) + PAD, infobar[sc].geo.height); + barwin_resize(infobar[sc].layout_button, ((conf.layout_button_width > 0) ? conf.layout_button_width : (textw(tags[sc][seltag[sc]].layout.symbol) + PAD)), infobar[sc].geo.height); barwin_refresh_color(infobar[sc].layout_button); if(tags[sc][seltag[sc]].layout.symbol) @@ -188,12 +188,12 @@ infobar_draw_selbar(int sc) else if(sel && !infobar[sc].selbar->mapped) barwin_map(infobar[sc].selbar); - if(conf.selbar.maxlenght >= 0 && sel) + if(conf.selbar.maxlength >= 0 && sel) { - str = emalloc(conf.selbar.maxlenght + 4, sizeof(char)); - strncpy(str, sel->title, conf.selbar.maxlenght); + str = emalloc(conf.selbar.maxlength + 4, sizeof(char)); + strncpy(str, sel->title, conf.selbar.maxlength); - if(strlen(sel->title) > conf.selbar.maxlenght) + if(strlen(sel->title) > conf.selbar.maxlength) strcat(str, "..."); } @@ -225,7 +225,7 @@ infobar_draw_taglist(int sc) Bool is_occupied[MAXTAG + 1]; if(conf.layout_placement) - barwin_move(infobar[sc].tags_board, textw(tags[sc][seltag[sc]].layout.symbol) + PAD * 1.5, 0); + barwin_move(infobar[sc].tags_board, ((conf.layout_button_width > 0) ? conf.layout_button_width : (textw(tags[sc][seltag[sc]].layout.symbol) + PAD)) + PAD / 2, 0); for(i = 0; i < MAXTAG; i++) is_occupied[i] = False; diff --git a/src/launcher.c b/src/launcher.c index 2f0d1fa..e2191f8 100644 --- a/src/launcher.c +++ b/src/launcher.c @@ -30,14 +30,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* conforming to glib use _GNU_SOURCE for asprintf declaration */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - #include "wmfs.h" - static char *complete_on_command(char*, size_t); static char *complete_on_files(char*, size_t); @@ -61,7 +55,7 @@ launcher_execute(Launcher *launcher) x = (conf.layout_placement) ? (infobar[selscreen].tags_board->geo.x + infobar[selscreen].tags_board->geo.width) - : (infobar[selscreen].layout_button->geo.x + textw(tags[selscreen][seltag[selscreen]].layout.symbol) + PAD); + : (infobar[selscreen].layout_button->geo.x + infobar[selscreen].layout_button->geo.width); XGrabKeyboard(dpy, ROOT, True, GrabModeAsync, GrabModeAsync, CurrentTime); @@ -256,7 +250,10 @@ complete_on_command(char *start, size_t hits) char *ret = NULL; DIR *dir; struct dirent *content; - size_t count = 0; + + char **namelist = NULL; + int n = 0, i; + void *temp = NULL; if (!getenv("PATH") || !start || hits <= 0) return NULL; @@ -265,27 +262,40 @@ complete_on_command(char *start, size_t hits) dirname = strtok(path, ":"); /* recursively open PATH */ - while (dirname) + while (dirname != NULL) { if ((dir = opendir(dirname))) { while ((content = readdir(dir))) - if (!strncmp(content->d_name, start, strlen(start)) && ++count == hits) + { + if(strncmp(content->d_name, ".", 1)) { - ret = _strdup(content->d_name + strlen(start)); - break; + if (!strncmp(content->d_name, start, strlen(start))) + { + temp = realloc(namelist, ++n * sizeof(*namelist)); + if ( temp != NULL ) + namelist = temp; + namelist[n-1] = strdup(content->d_name); + } } + } closedir(dir); } - - if (ret) - break; - dirname = strtok(NULL, ":"); } + qsort(namelist, n, sizeof(char *), qsort_string_compare); free(path); + if(n > 0) + { + ret = _strdup(namelist[((hits > 0) ? hits - 1 : 0) % n] + strlen(start)); + + for(i = 0; i < n; i++) + free(namelist[i]); + } + + free(namelist); return ret; } @@ -319,7 +329,7 @@ complete_on_files(char *start, size_t hits) { /* remplace ~ by $HOME in dirname */ if (!strncmp(p, "~/", 2) && getenv("HOME")) - asprintf(&dirname, "%s%s", getenv("HOME"), p+1); + xasprintf(&dirname, "%s%s", getenv("HOME"), p+1); else dirname = _strdup(p); @@ -350,12 +360,12 @@ complete_on_files(char *start, size_t hits) if (!strncmp(content->d_name, p, strlen(p)) && ++count == hits) { /* If it's a directory append '/' to the completion */ - asprintf(&filepath, "%s/%s", path, content->d_name); + xasprintf(&filepath, "%s/%s", path, content->d_name); if (filepath && stat(filepath, &st) != -1) { if (S_ISDIR(st.st_mode)) - asprintf(&ret, "%s/", content->d_name + strlen(p)); + xasprintf(&ret, "%s/", content->d_name + strlen(p)); else ret = _strdup(content->d_name + strlen(p)); } diff --git a/src/menu.c b/src/menu.c index a2b289a..ece0138 100644 --- a/src/menu.c +++ b/src/menu.c @@ -68,7 +68,11 @@ menu_draw(Menu menu, int x, int y) BarWindow *item[menu.nitem]; BarWindow *frame; - width = menu_get_longer_string(menu.item, menu.nitem) + PAD * 3; + int chcklen = 0; + if(menu_get_checkstring_needed(menu.item, menu.nitem)) + chcklen = textw(conf.selected_layout_symbol) + PAD / 3; + + width = menu_get_longer_string(menu.item, menu.nitem) + chcklen + textw(">") + PAD * 3; height = menu.nitem * (INFOBARH - SHADH); /* Frame barwin */ @@ -99,7 +103,7 @@ menu_draw(Menu menu, int x, int y) barwin_map(item[i]); barwin_refresh_color(item[i]); - menu_draw_item_name(&menu, i, item); + menu_draw_item_name(&menu, i, item, chcklen); barwin_refresh(item[i]); } @@ -209,6 +213,9 @@ Bool menu_activate_item(Menu *menu, int i) { int j, x, y; + int chcklen = 0; + if(menu_get_checkstring_needed(menu->item, menu->nitem)) + chcklen = textw(conf.selected_layout_symbol) + PAD / 3; if(menu->item[i].submenu) { @@ -216,7 +223,7 @@ menu_activate_item(Menu *menu, int i) if(!strcmp(menu->item[i].submenu, conf.menu[j].name)) { y = menu->y + ((i - 1) * INFOBARH + PAD) - SHADH * 2; - x = menu->x + menu_get_longer_string(menu->item, menu->nitem) + PAD * 3; + x = menu->x + menu_get_longer_string(menu->item, menu->nitem) + chcklen + textw(">") + PAD * 3; menu_draw(conf.menu[j], x, y); @@ -238,6 +245,10 @@ menu_focus_item(Menu *menu, int item, BarWindow *winitem[]) { int i; + int chcklen = 0; + if(menu_get_checkstring_needed(menu->item, menu->nitem)) + chcklen = textw(conf.selected_layout_symbol) + PAD / 3; + menu->focus_item = item; if(menu->focus_item > menu->nitem - 1) @@ -251,7 +262,7 @@ menu_focus_item(Menu *menu, int item, BarWindow *winitem[]) winitem[i]->bg = ((i == menu->focus_item) ? menu->colors.focus.bg : menu->colors.normal.bg); barwin_refresh_color(winitem[i]); - menu_draw_item_name(menu, i, winitem); + menu_draw_item_name(menu, i, winitem, chcklen); barwin_refresh(winitem[i]); } @@ -259,29 +270,29 @@ menu_focus_item(Menu *menu, int item, BarWindow *winitem[]) } void -menu_draw_item_name(Menu *menu, int item, BarWindow *winitem[]) +menu_draw_item_name(Menu *menu, int item, BarWindow *winitem[], int chcklen) { int x; - int width = menu_get_longer_string(menu->item, menu->nitem); + int width = menu_get_longer_string(menu->item, menu->nitem) + chcklen + PAD / 3; switch(menu->align) { case MA_Left: - x = PAD * 3 / 2; + x = chcklen + PAD / 2; break; case MA_Right: x = width - textw(menu->item[item].name) + PAD * 3 / 2; break; default: case MA_Center: - x = width / 2 - textw(menu->item[item].name) / 2 + PAD * 3 / 2; + x = (width - (chcklen + PAD / 3)) / 2 - textw(menu->item[item].name) / 2 + chcklen + PAD / 3; break; } - barwin_draw_text(winitem[item], x, FHINFOBAR, menu->item[item].name); + barwin_draw_image_ofset_text(winitem[item], x, FHINFOBAR, menu->item[item].name, chcklen + PAD / 2, 0); if(menu->item[item].check) if(menu->item[item].check(menu->item[item].cmd)) - barwin_draw_text(winitem[item], PAD / 3, FHINFOBAR, conf.selected_layout_symbol); + barwin_draw_image_ofset_text(winitem[item], PAD / 3, FHINFOBAR, conf.selected_layout_symbol, PAD / 3, 0); if(menu->item[item].submenu) barwin_draw_text(winitem[item], width + PAD * 2, FHINFOBAR, ">"); @@ -340,3 +351,9 @@ menu_clear(Menu *menu) return; } +Bool +menu_get_checkstring_needed(MenuItem *mi, int nitem) +{ + return True; +} + diff --git a/src/parse/api.c b/src/parse/api.c new file mode 100644 index 0000000..592dbf4 --- /dev/null +++ b/src/parse/api.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2010 Philippe Pepiot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _BSD_SOURCE +#include +#include +#include + +#include "parse.h" + +extern TAILQ_HEAD(, conf_sec) config; + +static const struct opt_type opt_type_null = { 0, 0, False, NULL }; + +static struct opt_type +string_to_opt(char *s) +{ + struct opt_type ret = opt_type_null; + + if (!s || !strlen(s)) + return ret; + + ret.num = strtol(s, (char**)NULL, 10); + ret.fnum = strtod(s, NULL); + + if (!strcmp(s, "true") || !strcmp(s, "True") || + !strcmp(s, "TRUE") || !strcmp(s, "1")) + ret.bool = True; + else + ret.bool = False; + + ret.str = s; + + return ret; +} + + +void +print_unused(struct conf_sec *sec) +{ + struct conf_sec *s; + struct conf_opt *o; + + if (!sec) + { + TAILQ_FOREACH(s, &config, entry) + print_unused(s); + return; + } + + SLIST_FOREACH(o, &sec->optlist, entry) + if (o->used == False) + warnx("%s:%d, unused param %s", + o->filename, o->line, o->name); + + TAILQ_FOREACH(s, &sec->sub, entry) + if (!TAILQ_EMPTY(&s->sub)) + print_unused(s); +} + +struct conf_sec ** +fetch_section(struct conf_sec *s, char *name) +{ + struct conf_sec **ret; + struct conf_sec *sec; + size_t i = 0; + + if (!name) + return NULL; + + if (!s) { + ret = xcalloc(2, sizeof(struct conf_sec *)); + TAILQ_FOREACH(sec, &config, entry) + if (!strcmp(sec->name, name)) { + ret[0] = sec; + ret[1] = NULL; + break; + } + } + else { + ret = xcalloc(s->nsub+1, sizeof(struct conf_sec *)); + TAILQ_FOREACH(sec, &s->sub, entry) { + if (!strcmp(sec->name, name) && i < s->nsub) + ret[i++] = sec; + } + ret[i] = NULL; + } + return ret; +} + +struct conf_sec * +fetch_section_first(struct conf_sec *s, char *name) +{ + struct conf_sec *sec, *ret = NULL; + + if (!name) + return NULL; + + if (!s) + { + TAILQ_FOREACH(sec, &config, entry) + if(sec->name && !strcmp(sec->name, name)) { + ret = sec; + break; + } + } + else + { + TAILQ_FOREACH(sec, &s->sub, entry) + if (sec->name && !strcmp(sec->name, name)) { + ret = sec; + break; + } + } + + return ret; +} + +size_t +fetch_section_count(struct conf_sec **s) +{ + size_t ret; + for (ret = 0; s[ret]; ret++); + return ret; +} + +struct opt_type * +fetch_opt(struct conf_sec *s, char *dfl, char *name) +{ + struct conf_opt *o; + struct opt_type *ret; + size_t i = 0; + + if (!name) + return NULL; + + ret = xcalloc(10, sizeof(struct opt_type)); + + if (s) { + SLIST_FOREACH(o, &s->optlist, entry) + if (!strcmp(o->name, name)) { + while (o->val[i]) { + o->used = True; + ret[i] = string_to_opt(o->val[i]); + i++; + } + ret[i] = opt_type_null; + return ret; + } + } + + ret[0] = string_to_opt(dfl); + ret[1] = opt_type_null; + + return ret; +} + +struct opt_type +fetch_opt_first(struct conf_sec *s, char *dfl, char *name) +{ + struct conf_opt *o; + + if (!name) + return opt_type_null; + else if (s) + SLIST_FOREACH(o, &s->optlist, entry) + if (!strcmp(o->name, name)) { + o->used = True; + return string_to_opt(o->val[0]); + } + return string_to_opt(dfl); +} + +size_t +fetch_opt_count(struct opt_type *o) +{ + size_t ret; + for(ret = 0; o[ret].str; ret++); + return ret; +} + + diff --git a/src/parse/parse.c b/src/parse/parse.c index 65dba6e..1f074a6 100644 --- a/src/parse/parse.c +++ b/src/parse/parse.c @@ -14,99 +14,182 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define _GNU_SOURCE #define _BSD_SOURCE #include #include #include +#include +#include #include #include +#include +#include #include #include +#include #include -#include "../wmfs.h" +#include "parse.h" extern char *__progname; -#define TOKEN(t) \ - do { \ - kw->type = (t); \ - TAILQ_INSERT_TAIL(&keywords, kw, entry); \ - kw = malloc(sizeof(*kw)); \ - } while (0) +enum keyword_t { SEC_START, SEC_END, INCLUDE, WORD, EQUAL, LIST_START, LIST_END, NONE }; -#define NEW_WORD() \ - do { \ - if (j > 0) { \ - e->name[j] = '\0'; \ - e->line = file.line; \ - TAILQ_INSERT_TAIL(&stack, e, entry); \ - e = malloc(sizeof(*e)); \ - j = 0; \ - TOKEN(WORD); \ - } \ - } while (0) +#ifdef DEBUG +static struct { + const char *name; + enum keyword_t type; +} kw_t_name[] = { + {"SEC_START", SEC_START}, + {"SEC_END", SEC_END}, + {"INCLUDE", INCLUDE}, + {"WORD", WORD}, + {"EQUAL", EQUAL}, + {"LIST_START", LIST_START}, + {"LIST_END", LIST_END}, + {"NONE", NONE}, +}; +#endif -enum conf_type { SEC_START, SEC_END, WORD, EQUAL, LIST_START, LIST_END, NONE }; - -struct conf_keyword { - enum conf_type type; - TAILQ_ENTRY(conf_keyword) entry; +struct files { + char *name; + struct files *parent; }; -struct conf_stack { - char name[BUFSIZ]; +struct keyword { + enum keyword_t type; + /* if WORD */ int line; - TAILQ_ENTRY(conf_stack) entry; + struct files *file; + char *name; + struct keyword *next; }; -struct conf_state { - Bool quote; - Bool comment; +struct state { + bool_t quote; + bool_t comment; char quote_char; }; -static void get_keyword(const char *buf, size_t n); -static void pop_keyword(void); -static void pop_stack(void); -static void syntax(const char *, ...); -static struct conf_sec *get_section(void); -static struct conf_opt *get_option(void); -static struct opt_type string_to_opt(char *); +/* TO REMOVE (use a identifier for config and fallback XDG in api functions) */ +TAILQ_HEAD(, conf_sec) config; +static struct keyword *keywords = NULL; + +static struct keyword * +push_keyword(struct keyword *tail, enum keyword_t type, char *buf, size_t *offset, struct files *file, int line) +{ + struct keyword *kw; + int i = 0; + + if (type == WORD && *offset == 0) + return tail; + + kw = xcalloc(1, sizeof(*kw)); + kw->type = type; + kw->line = line; + kw->file = file; + kw->next = NULL; + + if (*offset != 0) { + buf[*offset] = '\0'; + if (!strcmp(buf, INCLUDE_CMD)) + type = kw->type = INCLUDE; + else + kw->name = strdup(buf); + *offset = 0; + } + else + kw->name = NULL; + + if (tail) + tail->next = kw; + #ifdef DEBUG -static void print_kw_tree(void); -static char * get_kw_name(enum conf_type); + for (i = 0; kw_t_name[i].type != NONE; i++) { + if (kw_t_name[i].type == kw->type) { + warnx("%s %s %s:%d\n", kw_t_name[i].name, + (kw->name) ? kw->name : "", + kw->file->name, kw->line); + } + } #endif -static TAILQ_HEAD(, conf_keyword) keywords; -static TAILQ_HEAD(, conf_stack) stack; -static TAILQ_HEAD(, conf_sec) config; -static struct conf_keyword *curk; /* current keyword */ -static struct conf_stack *curw; /* current word */ -static const struct opt_type opt_type_null = { 0, 0, False, NULL }; - -static struct { - const char *name; - int line; -} file = { NULL, 1 }; + return kw; +} static void -get_keyword(const char *buf, size_t n) +syntax(struct keyword *kw, const char *fmt, ...) { - struct conf_keyword *kw; - size_t j, i; - struct conf_state s = { False, False, '\0' }; - struct conf_stack *e; + va_list args; - TAILQ_INIT(&stack); - TAILQ_INIT(&keywords); - kw = emalloc(1, sizeof(*kw)); - e = emalloc(1, sizeof(*e)); + fprintf(stderr, "%s:", __progname); + + if (kw && kw->file && kw->file->name) + fprintf(stderr, "%s:%d", kw->file->name, kw->line); + + if (kw && kw->name) + fprintf(stderr, ", near '%s'", kw->name); + fprintf(stderr, ": "); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fprintf(stderr, "\n"); +} + + +#define PUSH_KEYWORD(type) tail = push_keyword(tail, type, bufname, &j, file, line) +static struct keyword * +parse_keywords(const char *filename) +{ + int fd; + struct stat st; + char *buf; + + struct keyword *head = NULL; + struct keyword *tail = NULL; + struct files *file; + enum keyword_t type; /* keyword type to push */ + struct state s = { False, False, '\0'}; + char *bufname; + char path[PATH_MAX]; + size_t i, j; + int line; + bool_t error = False; + + + if ((fd = open(filename, O_RDONLY)) == -1 || stat(filename, &st) == -1) { + warn("%s", filename); + return NULL; + } + + if (!realpath(filename, path)) { + warn("%s", filename); + return NULL; + } + + file = xcalloc(1, sizeof(*file)); + bufname = xcalloc(1, sizeof(*bufname) * BUFSIZ); + file->name = strdup(path); + file->parent = NULL; + + buf = (char *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, SEEK_SET); + + if (buf == (char*)MAP_FAILED) { + warn("%s", filename); + return NULL; + } + + + for(i = 0, j = 0, line = 1; i < (size_t)st.st_size; i++) { + + if (!head && tail) + head = tail; - for(i = 0, j = 0; i < n; i++) - { if (buf[i] == '\n' && s.comment == True) { - file.line++; + line++; s.comment = False; continue; } @@ -119,506 +202,437 @@ get_keyword(const char *buf, size_t n) if (s.comment == True) continue; - if (buf[i] == s.quote_char && s.quote == True) { - NEW_WORD(); + if (s.quote == True && buf[i] == s.quote_char) { + /* end of quotted string */ + PUSH_KEYWORD(WORD); s.quote = False; continue; } - if ((buf[i] == '"' || buf[i] == '\'') && - s.quote == False) - { - s.quote_char = buf[i]; - s.quote = True; - continue; + if (s.quote == False) { + if ((buf[i] == '"' || buf[i] == '\'')) { + PUSH_KEYWORD(WORD); + /* begin quotted string */ + s.quote_char = buf[i]; + s.quote = True; + continue; + } + + if (buf[i] == '[') { + PUSH_KEYWORD(WORD); + if (buf[i+1] == '/') { + i +=2; + type = SEC_END; + } + else { + i++; + type = SEC_START; + } + + /* get section name */ + while (buf[i] != ']') { + + if (i >= ((size_t)st.st_size-1) || j >= (BUFSIZ-1)) { + bufname[j] = '\0'; + syntax(NULL, "word too long in %s:%d near '%s'", + file->name, line, bufname); + error = True; + break; + } + + bufname[j++] = buf[i++]; + } + PUSH_KEYWORD(type); + continue; + } + + if (buf[i] == '{') { + PUSH_KEYWORD(WORD); + PUSH_KEYWORD(LIST_START); + continue; + } + + if (buf[i] == '}') { + PUSH_KEYWORD(WORD); + PUSH_KEYWORD(LIST_END); + continue; + } + + if (buf[i] == ',') { + PUSH_KEYWORD(WORD); + continue; + } + + if (buf[i] == '=') { + PUSH_KEYWORD(WORD); + PUSH_KEYWORD(EQUAL); + continue; + } + + if (strchr("\t\n ", buf[i])) { + PUSH_KEYWORD(WORD); + + if (buf[i] == '\n') + line++; + + continue; + } + } /* s.quote == False */ + + if (j >= (BUFSIZ - 1)) { + bufname[j] = '\0'; + syntax(NULL, "word too long in %s:%d near '%s'", + file->name, line, bufname); + error = True; + break; } - if (buf[i] == '[' && s.quote == False) { - NEW_WORD(); - TOKEN((buf[i+1] == '/') ? SEC_END : SEC_START); - if (buf[i+1] == '/') - i++; - continue; - } - - if (buf[i] == ']' && s.quote == False) { - NEW_WORD(); - continue; - } - - if (buf[i] == '{' && s.quote == False) { - NEW_WORD(); - TOKEN(LIST_START); - continue; - } - - if (buf[i] == '}' && s.quote == False) { - NEW_WORD(); - TOKEN(LIST_END); - continue; - } - - if (buf[i] == ',' && s.quote == False) { - NEW_WORD(); - continue; - } - - if (buf[i] == '=' && s.quote == False) { - NEW_WORD(); - TOKEN(EQUAL); - continue; - } - - if (strchr("\t\n ", buf[i]) && s.quote == False) { - NEW_WORD(); - if (buf[i] == '\n') - file.line++; - continue; - } - - e->name[j++] = buf[i]; + bufname[j++] = buf[i]; } -} - -#ifdef DEBUG -static void -print_kw_tree(void) -{ - struct conf_keyword *k; - struct conf_stack *s; - - s = TAILQ_FIRST(&stack); - - TAILQ_FOREACH(k, &keywords, entry) - printf("%s ", get_kw_name(k->type)); - printf("\n"); -} - -static char * -get_kw_name(enum conf_type type) -{ - switch (type) { - case SEC_START: - return ("SEC_START"); - break; - case SEC_END: - return ("SEC_END"); - break; - case WORD: - return ("WORD"); - break; - case LIST_START: - return ("LIST_START "); - break; - case LIST_END: - return ("LIST_END "); - break; - case EQUAL: - return ("EQUAL "); - break; - default: - return ("NONE "); - break; - } -} -#endif - -int -get_conf(const char *name) -{ - int fd; - struct stat st; - char *buf; - struct conf_sec *s; - - if (!name) - return (-1); - - if ((fd = open(name, O_RDONLY)) == -1 || - stat(name, &st) == -1) - { - warn("%s", name); - return (-1); - } - - buf = (char*)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, SEEK_SET); - - if (buf == (char*) MAP_FAILED) - return -1; - - get_keyword(buf, st.st_size); munmap(buf, st.st_size); - close(fd); - warnx("%s read", name); + free(bufname); + warnx("%s read", file->name); - file.name = name; + return (error ? NULL: head); +} - curk = TAILQ_FIRST(&keywords); - curw = TAILQ_FIRST(&stack); +/* + * return NULL on failure and head->next if + * no config found (of file doesn't exist) + * NOTE to devs: head->name is the file to include + */ +static struct keyword * +include(struct keyword *head) +{ + struct keyword *kw; + struct keyword *tail; + struct files *file; + struct passwd *user; + char *filename = NULL; + char *base = NULL; + + head = head->next; + + if (!head || head->type != WORD) { + syntax(head, "missing filename to include"); + return NULL; + } + + /* replace ~ by user directory */ + if (head->name && head->name[0] == '~') { + if ( (user = getpwuid(getuid())) && user->pw_dir) + xasprintf(&filename, "%s%s", user->pw_dir, head->name+1); + else if (getenv("HOME")) + xasprintf(&filename, "%s%s", getenv("HOME"), head->name+1); + else /* to warning ? */ + filename = head->name; + } + /* relative path from parent file */ + else if (head->name && head->name[0] != '/') { + base = strdup(kw->file->parent->name); + xasprintf(&filename, "%s/%s", dirname(base), head->name); + free(base); + } + else + filename = head->name; + + if (!(kw = parse_keywords(filename))) { + warnx("no config fond in include file %s", head->name); + + if (filename != head->name) + free(filename); + + return NULL; + } + + kw->file->parent = head->file; + + /* detect circular include */ + for (file = kw->file->parent; file != NULL; file = file->parent) { + if (!strcmp(file->name, kw->file->name)) { + syntax(kw, "circular include of %s", kw->file->name); + + if (filename != head->name) + free(filename); + + return NULL; + } + } + + head = head->next; + + if (kw) { + for (tail = kw; tail->next; tail = tail->next); + tail->next = head; + } + + return kw; +} + +static void * +free_opt(struct conf_opt *o) +{ + if (o) + free(o); + return NULL; +} + +static struct conf_opt * +get_option(struct keyword **head) +{ + struct conf_opt *o; + size_t j = 0; + struct keyword *kw = *head; + + o = xcalloc(1, sizeof(*o)); + o->name = kw->name; + o->used = False; + o->line = kw->line; + o->filename = kw->file->name; + + kw = kw->next; + + if (kw->type != EQUAL) { + syntax(kw, "missing '=' here"); + return free_opt(o); + } + + kw = kw->next; + + if (!kw) { + syntax(kw, "missing value"); + return free_opt(o); + } + + + switch (kw->type) { + case INCLUDE: + if (!(kw = include(kw))) + return free_opt(o); + break; + case WORD: + o->val[0] = kw->name; + o->val[1] = NULL; + kw = kw->next; + break; + case LIST_START: + kw = kw->next; + while (kw && kw->type != LIST_END) { + switch (kw->type) { + case WORD: + if (j > (PARSE_MAX_LIST - 1)) { + syntax(kw, "too much values in list"); + return free_opt(o); + } + o->val[j++] = kw->name; + kw = kw->next; + break; + case INCLUDE: + if (!(kw = include(kw))) + return free_opt(o); + break; + default: + syntax(kw, "declaration into a list"); + return free_opt(o); + break; + } + } + + if (!kw) { + syntax(kw, "list unclosed"); + return free_opt(o); + } + + kw = kw->next; + break; + default: + syntax(kw, "missing value"); + return free_opt(o); + break; + } + + *head = kw; + return o; +} + +static void * +free_sec(struct conf_sec *sec) +{ + struct conf_opt *o; + struct conf_sec *s; + + if (sec) { + while (!SLIST_EMPTY(&sec->optlist)) { + o = SLIST_FIRST(&sec->optlist); + SLIST_REMOVE_HEAD(&sec->optlist, entry); + free_opt(o); + } + while (!TAILQ_EMPTY(&sec->sub)) { + s = TAILQ_FIRST(&sec->sub); + TAILQ_REMOVE(&sec->sub, s, entry); + free_sec(s); + } + free(sec); + } + return NULL; +} + +static struct conf_sec * +get_section(struct keyword **head) +{ + struct conf_sec *s; + struct conf_opt *o; + struct conf_sec *sub; + struct keyword *kw = *head; + + s = xcalloc(1, sizeof(*s)); + s->name = kw->name; + TAILQ_INIT(&s->sub); + SLIST_INIT(&s->optlist); + + kw = kw->next; + + while (kw && kw->type != SEC_END) { + switch (kw->type) { + case INCLUDE: + if (!(kw = include(kw))) + return free_sec(s); + break; + case SEC_START: + if (!(sub = get_section(&kw))) + return free_sec(s); + TAILQ_INSERT_TAIL(&s->sub, sub, entry); + s->nsub++; + break; + case WORD: + if (!(o = get_option(&kw))) + return free_sec(s); + SLIST_INSERT_HEAD(&s->optlist, o, entry); + s->nopt++; + break; + default: + syntax(kw, "syntax error"); + return free_sec(s); + break; + } + } + + if (!kw || strcmp(kw->name, s->name)) { + syntax(kw, "missing end section %s", s->name); + return free_sec(s); + } + + kw = kw->next; + *head = kw; + + return s; +} + +int +free_conf(void) +{ + struct conf_sec *s; + struct keyword *kw, *nkw; + struct files **f = NULL; + int i, nf = 0; + + while (!TAILQ_EMPTY(&config)) { + s = TAILQ_FIRST(&config); + TAILQ_REMOVE(&config, s, entry); + free_sec(s); + } + + kw = keywords; + + while (kw) { + nkw = kw->next; + + if (kw->name) + free(kw->name); + + for (i = 0; i < nf; i++) { + if (f[i] == kw->file) { + if (!(f = realloc(f, sizeof(*f) * (++i)))) + err(EXIT_FAILURE, "realloc"); + f[i-1] = kw->file; + } + } + + kw = nkw; + } + + if (nf > 0) { + for (i = 0; i < nf; i++) { + free(f[i]->name); + free(f[i]); + } + free(f); + } + return -1; +} + +int +get_conf(const char *filename) +{ + struct conf_sec *s; + struct keyword *head, *kw; + + kw = head = parse_keywords(filename); + + if (!head) + return -1; /* TODO ERREUR */ + + keywords = head; TAILQ_INIT(&config); - while (!TAILQ_EMPTY(&keywords)) { - switch (curk->type) { + while (kw) { + switch (kw->type) { + case INCLUDE: + if (!(kw = include(kw))) + return free_conf(); + break; case SEC_START: - s = get_section(); + if (!(s = get_section(&kw))) + return free_conf(); TAILQ_INSERT_TAIL(&config, s, entry); break; default: - syntax("out of any section"); + syntax(kw, "out of any section"); + return free_conf(); break; } } return 0; } -static struct conf_sec * -get_section(void) + +/* calloc wrapper */ +void * +xcalloc(size_t nmemb, size_t size) { - struct conf_sec *s; - struct conf_opt *o; - struct conf_sec *sub; + void *ret; - s = emalloc(1, sizeof(*s)); - s->name = strdup(curw->name); - TAILQ_INIT(&s->sub); - SLIST_INIT(&s->optlist); + if (!(ret = calloc(nmemb, size))) + err(EXIT_FAILURE, "calloc"); - pop_stack(); - pop_keyword(); - - if (!curk || curk->type != WORD) - syntax("missing section name"); - pop_keyword(); - - while (!TAILQ_EMPTY(&keywords) && curk->type != SEC_END) { - switch (curk->type) { - case WORD: - o = get_option(); - SLIST_INSERT_HEAD(&s->optlist, o, entry); - s->nopt++; - break; - case SEC_START: - sub = get_section(); - TAILQ_INSERT_TAIL(&s->sub, sub, entry); - s->nsub++; - case SEC_END: - break; - default: - syntax("syntax error"); - break; - } - } - pop_keyword(); - - if (curk && curk->type != WORD) - syntax("missing end-section name"); - - if (!curk || strcmp(curw->name, s->name)) - syntax("non-closed section '%s'", s->name); - - pop_stack(); - pop_keyword(); - return s; + return ret; } - -static struct conf_opt * -get_option(void) -{ - struct conf_opt *o; - size_t j = 0; - - o = emalloc(1, sizeof(*o)); - o->name = strdup(curw->name); - o->used = False; - o->line = curw->line; - pop_stack(); - pop_keyword(); - - if (!curk || curk->type != EQUAL) - syntax("missing '=' here"); - - pop_keyword(); - - if (!curk) - syntax("syntax error"); - - switch (curk->type) { - case WORD: - o->val[0] = strdup(curw->name); - o->val[1] = NULL; - pop_stack(); - break; - case LIST_START: - pop_keyword(); - while (curk && curk->type != LIST_END) { - if (curk->type != WORD) - syntax("declaration into a list"); - o->val[j++] = strdup(curw->name); - pop_stack(); - pop_keyword(); - } - o->val[j] = NULL; - break; - default: - syntax("syntax error"); - break; - } - pop_keyword(); - return o; -} - - -static void -pop_keyword(void) -{ - if (curk) - { - TAILQ_REMOVE(&keywords, curk, entry); -#ifdef DEBUG - warnx("%s", get_kw_name(curk->type)); -#endif - free(curk); - - curk = TAILQ_FIRST(&keywords); - } -} - -static void -pop_stack(void) -{ - if (curw) - { - TAILQ_REMOVE(&stack, curw, entry); -#ifdef DEBUG - warnx("%s", curw->name); -#endif - free(curw); - - curw = TAILQ_FIRST(&stack); - } -} - -static void -syntax(const char *fmt, ...) +/* asprintf wrapper */ +int +xasprintf(char **strp, const char *fmt, ...) { + int ret; va_list args; - if (curw) - fprintf(stderr, "%s: %s:%d, near '%s', ", - __progname, file.name, curw->line, curw->name); - else - fprintf(stderr, "%s: %s: ", __progname, file.name); va_start(args, fmt); - vfprintf(stderr, fmt, args); + ret = vasprintf(strp, fmt, args); va_end(args); - exit(EXIT_FAILURE); -} -void -print_unused(struct conf_sec *sec) -{ - struct conf_sec *s; - struct conf_opt *o; - - if (!sec) - { - TAILQ_FOREACH(s, &config, entry) - print_unused(s); - return; - } - - SLIST_FOREACH(o, &sec->optlist, entry) - if (o->used == False) - warnx("%s:%d, unused param %s", - file.name, o->line, o->name); - - TAILQ_FOREACH(s, &sec->sub, entry) - if (!TAILQ_EMPTY(&s->sub)) - print_unused(s); -} - -void -free_conf(struct conf_sec *sec) -{ - struct conf_sec *s; - struct conf_opt *o; - size_t n; - - if (!sec) - { - TAILQ_FOREACH(s, &config, entry) - { - free(s->name); - free_conf(s); - free(s); - } - return; - } - - while (!SLIST_EMPTY(&sec->optlist)) - { - o = SLIST_FIRST(&sec->optlist); - SLIST_REMOVE_HEAD(&sec->optlist, entry); - free(o->name); - - for (n = 0; o->val[n]; n++) - free(o->val[n]); - - free(o); - } - - while (!TAILQ_EMPTY(&sec->sub)) - { - s = TAILQ_FIRST(&sec->sub); - TAILQ_REMOVE(&sec->sub, s, entry); - free_conf(s); - } - -} - -struct conf_sec ** -fetch_section(struct conf_sec *s, char *name) -{ - struct conf_sec **ret; - struct conf_sec *sec; - size_t i = 0; - - if (!name) - return NULL; - - if (!s) { - ret = emalloc(2, sizeof(struct conf_sec *)); - TAILQ_FOREACH(sec, &config, entry) - if (!strcmp(sec->name, name)) { - ret[0] = sec; - ret[1] = NULL; - break; - } - } - else { - ret = emalloc(s->nsub+1, sizeof(struct conf_sec *)); - TAILQ_FOREACH(sec, &s->sub, entry) { - if (!strcmp(sec->name, name) && i < s->nsub) - ret[i++] = sec; - } - ret[i] = NULL; - } - return ret; -} - -struct conf_sec * -fetch_section_first(struct conf_sec *s, char *name) -{ - struct conf_sec *sec, *ret = NULL; - - if (!name) - return NULL; - - if (!s) - { - TAILQ_FOREACH(sec, &config, entry) - if(sec->name && !strcmp(sec->name, name)) { - ret = sec; - break; - } - } - else - { - TAILQ_FOREACH(sec, &s->sub, entry) - if (sec->name && !strcmp(sec->name, name)) { - ret = sec; - break; - } - } + if (ret == -1) + err(EXIT_FAILURE, "asprintf"); return ret; } - -size_t -fetch_section_count(struct conf_sec **s) -{ - size_t ret; - for (ret = 0; s[ret]; ret++); - return ret; -} - -struct opt_type * -fetch_opt(struct conf_sec *s, char *dfl, char *name) -{ - struct conf_opt *o; - struct opt_type *ret; - size_t i = 0; - - if (!name) - return NULL; - - ret = emalloc(10, sizeof(struct opt_type)); - - if (s) { - SLIST_FOREACH(o, &s->optlist, entry) - if (!strcmp(o->name, name)) { - while (o->val[i]) { - o->used = True; - ret[i] = string_to_opt(o->val[i]); - i++; - } - ret[i] = opt_type_null; - return ret; - } - } - - ret[0] = string_to_opt(dfl); - ret[1] = opt_type_null; - - return ret; -} - -struct opt_type -fetch_opt_first(struct conf_sec *s, char *dfl, char *name) -{ - struct conf_opt *o; - - if (!name) - return opt_type_null; - else if (s) - SLIST_FOREACH(o, &s->optlist, entry) - if (!strcmp(o->name, name)) { - o->used = True; - return string_to_opt(o->val[0]); - } - return string_to_opt(dfl); -} - -size_t -fetch_opt_count(struct opt_type *o) -{ - size_t ret; - for(ret = 0; o[ret].str; ret++); - return ret; -} - -static struct opt_type -string_to_opt(char *s) -{ - struct opt_type ret = opt_type_null; - - if (!s || !strlen(s)) - return ret; - - ret.num = strtol(s, (char**)NULL, 10); - ret.fnum = strtod(s, NULL); - - if (!strcmp(s, "true") || !strcmp(s, "True") || - !strcmp(s, "TRUE") || !strcmp(s, "1")) - ret.bool = True; - else - ret.bool = False; - - ret.str = s; - - return ret; -} - diff --git a/src/parse/parse.h b/src/parse/parse.h index 78bc737..8a0f9f2 100644 --- a/src/parse/parse.h +++ b/src/parse/parse.h @@ -19,12 +19,22 @@ #include +#define INCLUDE_CMD "@include" +#define PARSE_MAX_LIST 10 + +#if defined(Bool) +#define bool_t Bool +#else +typedef enum { False, True } bool_t; +#endif /* Bool */ + struct conf_opt { char *name; - char *val[10]; + char *val[PARSE_MAX_LIST]; size_t nval; - Bool used; + bool_t used; int line; + char *filename; SLIST_ENTRY(conf_opt) entry; }; @@ -40,7 +50,7 @@ struct conf_sec { struct opt_type { long int num; float fnum; - Bool bool; + bool_t bool; char *str; }; @@ -61,7 +71,7 @@ void print_unused(struct conf_sec *s); * WARNING: This make all string * returned by fetch_(opt|section)(_first) unusable. */ -void free_conf(struct conf_sec *s); +int free_conf(void); /* * Get all subsection matching the given name on the given @@ -107,4 +117,10 @@ struct opt_type *fetch_opt(struct conf_sec *, char *, char *); */ size_t fetch_opt_count(struct opt_type *); + +/* wrapper for calloc */ +void *xcalloc(size_t, size_t); +/* wrapper for asprintf */ +int xasprintf(char **, const char *, ...); + #endif /* PARSE_H */ diff --git a/src/structs.h b/src/structs.h index 5da0fe4..2e78232 100644 --- a/src/structs.h +++ b/src/structs.h @@ -352,6 +352,18 @@ typedef struct char *content; } Alias; +/* Rule struct */ +typedef struct +{ + char *class; + char *instance; + char *role; + int screen; + int tag; + Bool free; + Bool max; +} Rule; + /* Configuration structure */ typedef struct { @@ -402,7 +414,7 @@ typedef struct { char *fg; uint bg; - int maxlenght; + int maxlength; MouseBinding *mouse; int nmouse; } selbar; @@ -457,9 +469,11 @@ typedef struct } systray; Alias alias[256]; uint mouse_tag_action[TagActionLast]; + int layout_button_width; Layout layout[NUM_OF_LAYOUT]; Menu *menu; Launcher *launcher; + Rule *rule; int *ntag; Bool tag_round; Bool client_round; @@ -472,6 +486,7 @@ typedef struct int nlayout; int nmenu; int nlauncher; + int nrule; } Conf; typedef struct diff --git a/src/util.c b/src/util.c index e1e8b1c..31807ca 100644 --- a/src/util.c +++ b/src/util.c @@ -241,7 +241,10 @@ spawn(const char *format, ...) execl(sh, sh, "-c", cmd, (char*)NULL); exit(EXIT_FAILURE); } - write(p[1], &pid, sizeof(pid_t)); + + if (sizeof(pid_t) != write(p[1], &pid, sizeof(pid_t))) + warn("write"); + close(p[1]); exit(EXIT_SUCCESS); } @@ -356,3 +359,10 @@ patht(char *path) return ret; } + +int +qsort_string_compare (const void * a, const void * b) +{ + return (strcmp(*(char **)a, *(char **)b)); +} + diff --git a/src/wmfs.c b/src/wmfs.c index 5ccb47e..d1c666a 100644 --- a/src/wmfs.c +++ b/src/wmfs.c @@ -118,6 +118,7 @@ quit(void) } IFREE(conf.launcher); + IFREE(conf.rule); IFREE(conf.bars.mouse); IFREE(conf.selbar.mouse); @@ -125,7 +126,7 @@ quit(void) IFREE(conf.client.mouse); IFREE(conf.root.mouse); - free_conf(NULL); + free_conf(); XSync(dpy, False); XCloseDisplay(dpy); diff --git a/src/wmfs.h b/src/wmfs.h index f3c0033..c5da5c1 100644 --- a/src/wmfs.h +++ b/src/wmfs.h @@ -34,7 +34,7 @@ #define WMFS_H #define _BSD_SOURCE /* vsnprintf */ -#define _POSIX_SOURCE /* kill */ + /* Lib headers */ #include #include @@ -122,6 +122,7 @@ BarWindow *barwin_create(Window parent, Bool stipple, Bool border); void barwin_draw_text(BarWindow *bw, int x, int y, char *text); +void barwin_draw_image_ofset_text(BarWindow *bw, int x, int y, char *text, int x_image_ofset, int y_image_ofset); void barwin_delete(BarWindow *bw); void barwin_delete_subwin(BarWindow *bw); void barwin_map(BarWindow *bw); @@ -135,6 +136,7 @@ void barwin_refresh(BarWindow *bw); /* draw.c */ void draw_text(Drawable d, int x, int y, char* fg, int pad, char *str); +void draw_image_ofset_text(Drawable d, int x, int y, char* fg, int pad, char *str, int x_image_ofset, int y_image_ofset); void draw_rectangle(Drawable dr, int x, int y, uint w, uint h, uint color); void draw_graph(Drawable dr, int x, int y, uint w, uint h, uint color, char *data); @@ -258,10 +260,11 @@ void menu_draw(Menu menu, int x, int y); Bool menu_manage_event(XEvent *ev, Menu *menu, BarWindow *winitem[]); Bool menu_activate_item(Menu *menu, int i); void menu_focus_item(Menu *menu, int item, BarWindow *winitem[]); -void menu_draw_item_name(Menu *menu, int item, BarWindow *winitem[]); +void menu_draw_item_name(Menu *menu, int item, BarWindow *winitem[], int chcklen); int menu_get_longer_string(MenuItem *mi, int nitem); void uicb_menu(uicb_t cmd); void menu_clear(Menu *menu); +Bool menu_get_checkstring_needed(MenuItem *mi, int nitem); /* launcher.c */ void launcher_execute(Launcher *launcher); @@ -297,7 +300,7 @@ void swap_ptr(void **x, void **y); void uicb_spawn(uicb_t); char *clean_value(char *str); char* patht(char *path); - +int qsort_string_compare (const void * a, const void * b); #ifdef HAVE_IMLIB int parse_image_block(ImageAttr *im, char *str); diff --git a/wmfs.doxygen.in b/wmfs.doxygen.in index 602c1fc..583a59b 100644 --- a/wmfs.doxygen.in +++ b/wmfs.doxygen.in @@ -73,7 +73,7 @@ WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = @SOURCE_DIR@/src +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/src INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c \ *.h \ diff --git a/wmfsrc.in b/wmfsrc.in index b58ee46..a1170c3 100644 --- a/wmfsrc.in +++ b/wmfsrc.in @@ -3,6 +3,9 @@ # ~/.config/wmfs/wmfsrc and edit it. # +# Include file to split configuration +# @include "~/.config/wmfs/menu-wmfsrc" + [misc] font = "dejavu-10" raisefocus = false @@ -36,8 +39,8 @@ bg = "#191919" fg = "#D4D4ff" - # Cut title lenght - # max_lenght = 25 + # Cut title length + # max_length = 25 [mouse] button = "3" func = "clientlist" [/mouse] [mouse] button = "4" func = "client_next" [/mouse] @@ -65,6 +68,9 @@ # Symbol displayed for the selected layout in the list selected_layout_symbol = "*" + # Width of layout button + # layout_button_width = x + # Tiling layouts. [layout] type = "tile_right" symbol = "RIGHT" [/layout] [layout] type = "tile_left" symbol = "LEFT" [/layout] @@ -127,6 +133,7 @@ #[mouse] [/mouse] Possible multi mouse section [/tag] + # clients option is *DEPRECATED* but works, see [rules] section [tag] name = "two" clients = {"Browser"} [/tag] [tag] name = "three" [/tag] [tag] name = "four" [/tag] @@ -161,6 +168,7 @@ # Modifier for mouse use modifier = "Alt" + # *DEPRECATED* but works, see [rules] section # Set automatic free or max client # autofree = "xterm|MPlayer" # automax = "Navigator" @@ -195,6 +203,19 @@ [/titlebar] [/client] +[rules] + # Example of rule for Mplayer + [rule] + instance = "xv" # First part of WM_CLASS + class = "MPlayer" # Seconf part of WM_CLASS, not needed if first part is correct + # role = "" # WM_WINDOW_ROLE + screen = 0 # Screen to use + tag = 2 # Tag number of apps + free = true # Set automatic free client + max = false # Set automatic maximized client + [/rule] +[/rules] + [menu] # Default menu, binded on the root window, button 3. [set_menu]