subtle-0.11.3224-xi/0000755000175000017500000000000011770527221014214 5ustar formorerformorersubtle-0.11.3224-xi/Doxyfile0000644000175000017500000002072511770332063015726 0ustar formorerformorer# Doxyfile 1.5.4 # $Id$ #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = subtle PROJECT_NUMBER = OUTPUT_DIRECTORY = docs CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO DETAILS_AT_TOP = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 2 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES TYPEDEF_HIDES_STRUCT = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = src config sublets INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.h *.c *.rb RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO HTML_DYNAMIC_SECTIONS = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = YES DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = YES TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO subtle-0.11.3224-xi/Rakefile0000644000175000017500000004516311770332063015670 0ustar formorerformorer# -*- encoding: utf-8 -*- # # @package subtle # # @file Rake build file # @copyright (c) 2005-2012 Christoph Kappel # @version $Id$ # # This program can be distributed under the terms of the GNU GPL. # See the file COPYING. # require "mkmf" require "yaml" require "rake/clean" require "digest/md5" # FIXME: RDoc version crap begin require "rdoc/task" rescue LoadError require "rake/rdoctask" module RDoc class Task < Rake::RDocTask end end end # FIXME: mkmf redefines #rm_f for splats and rake (>=0.9.1) # includes FileUtils and just dispatches to it module Rake module FileUtilsExt def rm_f(*files) # Copied from mkmf.rb:197 opt = (Hash === files.last ? [files.pop] : []) FileUtils.rm_f(Dir[*files.flatten], *opt) end end end # Settings # Options / defines {{{ @options = { "cc" => ENV["CC"] || "gcc", "destdir" => ENV["DESTDIR"] || "", "prefix" => "/usr", "manprefix" => "$(prefix)/share/man", "bindir" => "$(destdir)/$(prefix)/bin", "sysconfdir" => "$(destdir)/etc", "configdir" => "$(sysconfdir)/xdg/$(PKG_NAME)", "datadir" => "$(destdir)/$(prefix)/share/$(PKG_NAME)", "extdir" => "$(destdir)/$(sitelibdir)/$(PKG_NAME)", "mandir" => "$(destdir)/$(manprefix)/man1", "debug" => "no", "xpm" => "yes", "xft" => "yes", "xinerama" => "yes", "xrandr" => "yes", "xtest" => "yes", "builddir" => "build", "hdrdir" => "", "archdir" => "", "revision" => "3224", #< Latest stable "cflags" => "-Wall -Werror -Wpointer-arith -Wstrict-prototypes -Wunused -Wshadow -std=gnu99", "cpppath" => "-I. -I$(builddir) -Isrc -Isrc/shared -Isrc/subtle -idirafter$(hdrdir) -idirafter$(archdir)", "ldflags" => "-L$(libdir) $(rpath) $(LIBS) -l$(RUBY_SO_NAME)", "extflags" => "$(LDFLAGS) $(rpath) $(LIBS) -l$(RUBY_SO_NAME)", "rpath" => "-L$(libdir) -Wl,-rpath=$(libdir)", "checksums" => [] } @defines = { "PKG_NAME" => "subtle", "PKG_VERSION" => "0.11.$(revision)", "PKG_BUGREPORT" => "http://subforge.org/projects/subtle/issues", "PKG_CONFIG" => "subtle.rb", "RUBY_VERSION" => "$(MAJOR).$(MINOR).$(TEENY)" } # }}} # Lists {{{ PG_SUBTLE = "subtle" PG_SUBTLEXT = "subtlext" PG_SUBTLER = "subtler" PG_SUR = "sur" PG_SERVER = "surserver" SRC_SHARED = FileList["src/shared/*.c"] SRC_SUBTLE = (SRC_SHARED | FileList["src/subtle/*.c"]) SRC_SUBTLEXT = (SRC_SHARED | FileList["src/subtlext/*.c"]) # Collect object files OBJ_SUBTLE = SRC_SUBTLE.collect do |f| File.join(@options["builddir"], "subtle", File.basename(f).ext("o")) end OBJ_SUBTLEXT = SRC_SUBTLEXT.collect do |f| File.join(@options["builddir"], "subtlext", File.basename(f).ext("o")) end FUNCS = [ "select" ] HEADER = [ "stdio.h", "stdlib.h", "stdarg.h", "string.h", "unistd.h", "signal.h", "errno.h", "assert.h", "sys/time.h", "sys/types.h" ] OPTIONAL = [ "sys/inotify.h", "wordexp.h" ] # }}} # Miscellaneous {{{ Logging.logfile("config.log") #< mkmf log CLEAN.include(PG_SUBTLE, "#{PG_SUBTLEXT}.so", OBJ_SUBTLE, OBJ_SUBTLEXT) CLOBBER.include(@options["builddir"], "config.h", "config.log", "config.yml") # }}} # Funcs ## silent_sh {{{ # Wraper to suppress the output of shell commands # @param [String] cmd Command name # @param [String] msg Command replacement message # @param [Block] block Command block ## def silent_sh(cmd, msg, &block) # FIXME: Hide raw messages in 0.8.7 and 0.9.1 unless true === RakeFileUtils.verbose or (defined? Rake::FileUtilsExt and Rake::FileUtilsExt.respond_to? :verbose_flag and true === Rake::FileUtilsExt.verbose_flag) rake_output_message(msg) else rake_output_message(Array == cmd.class ? cmd.join(" ") : cmd) #< Check type end unless RakeFileUtils.nowrite res = system(cmd) block.call(res, $?) end end # }}} ## make_header {{{ # Create config header # @param [String] header Name of the header ## def make_header(header = "config.h") message "creating %s\n", header sym = header.tr("a-z./\055", "A-Z___") hdr = ["#ifndef #{sym}\n#define #{sym}\n"] # Create lines from defitions for line in $defs case line when /^-D([^=]+)(?:=(.*))?/ hdr << "#define #$1 #{$2 ? $2 : 1}\n" when /^-U(.*)/ hdr << "#undef #$1\n" end end hdr << "#endif\n" hdr = hdr.join unless(IO.read(header) == hdr rescue false) open(header, "w") do |hfile| hfile.write(hdr) end end end # }}} ## make_config {{{ # Create config file # @param [String] file Name of config file ## def make_config(file = "config.yml") message("Creating %s\n" % [ file ]) yaml = [@options, @defines].to_yaml # Dump yaml File.open(file, "w+") do |out| YAML::dump(yaml, out) end end # }}} ## compile {{{ # Wrapper to suppress compiler output # @param [String] src Input filename # @param [String] out Output filename # @param [String] options Addiotional compiler options ## def compile(src, out = nil, options = "") out = File.join(@options["builddir"], File.basename(src).ext("o")) if out.nil? opt = ["shared.c", "subtlext.c"].include?(File.basename(src)) ? " -fPIC " : "" opt << options # Suppress default output silent_sh("#{@options["cc"]} -o #{out} -c #{@options["cflags"]} #{opt} #{@options["cpppath"]} #{src}", "CC #{out}") do |ok, status| ok or fail("Compiler failed with status #{status.exitstatus}") end end # }}} ## checksums # {{{ # Create and check checksums of header files ## def checksums ret = true # Create checksums files = Dir["src/*/*.h"] sums = files.map { |h| Digest::MD5.file(h).to_s } # Call clean task when sums don't match if((@options["checksums"] - sums).any? rescue true) Rake::Task["clean"].invoke ret = false end @options["checksums"] = sums ret end # }}} # Tasks ## default {{{ # Default task for rake ## desc("Configure and build subtle") task(:default => [:config, :build]) # }}} ## config {{{ # Configure build environment ## desc("Configure subtle") task(:config) do # Check if build dirs exist [ File.join(@options["builddir"], "subtle"), File.join(@options["builddir"], "subtlext") ].each do |dir| FileUtils.mkdir_p(dir) unless File.exist?(dir) end # Check if options.yaml exists or config is run explicitly if( !ARGV.nil? and !ARGV.include?("config")) and File.exist?("config.yml") yaml = YAML::load(File.open("config.yml")) @options, @defines = YAML::load(yaml) make_config unless checksums else # Check version if 1 != RbConfig::CONFIG["MAJOR"].to_i or 9 != RbConfig::CONFIG["MINOR"].to_i fail("Ruby 1.9.0 or higher required") end checksums # Update rpath @options["rpath"] = RbConfig.expand(@options["rpath"]) # Get options @options.each_key do |k| unless ENV[k].nil? @options[k] = ENV[k] end end # Debug if "yes" == @options["debug"] @options["cflags"] << " -g -DDEBUG" else @options["cflags"] << " -DNDEBUG" end # Get revision if File.exists?(".hg") and (hg = find_executable0("hg")) match = `#{hg} tip`.match(/^[^:]+:\s*(\d+).*/) if !match.nil? and 2 == match.size @options["revision"] = match[1] end end # Get ruby header dir if RbConfig::CONFIG["rubyhdrdir"].nil? @options["hdrdir"] = RbConfig.expand( RbConfig::CONFIG["archdir"] ) else @options["hdrdir"] = RbConfig.expand( RbConfig::CONFIG["rubyhdrdir"] ) end @options["archdir"] = File.join( @options["hdrdir"], RbConfig.expand(RbConfig::CONFIG["arch"]) ) # Expand options and defines @options["sitelibdir"] = RbConfig.expand( RbConfig::CONFIG["sitelibdir"] ) [@options, @defines].each do |hash| hash.each do |k, v| if v.is_a?(String) hash[k] = RbConfig.expand( v, CONFIG.merge(@options.merge(@defines)) ) end end end # Check arch if RbConfig::CONFIG["arch"].match(/openbsd/) $defs.push("-DIS_OPENBSD") end # Check header HEADER.each do |h| fail("Header #{h} was not found") unless have_header(h) end # Check optional headers OPTIONAL.each do |h| have_header(h) end # Check execinfo checking_for("execinfo.h") do ret = false lib = " -lexecinfo" # Check if execinfo is a separate lib (freebsd) if find_header("execinfo.h") if try_func("backtrace", "") $defs.push("-DHAVE_EXECINFO_H") ret = true elsif try_func("backtrace", lib) @options["ldflags"] << lib $defs.push("-DHAVE_EXECINFO_H") ret = true end end ret end # Check pkg-config for X11 checking_for("X11/Xlib.h") do cflags, ldflags, libs = pkg_config("x11") fail("X11 was not found") if libs.nil? # Update flags @options["cflags"] << " %s" % [ cflags ] @options["ldflags"] << " %s %s" % [ ldflags, libs ] @options["extflags"] << " %s %s" % [ ldflags, libs ] true end # Check pkg-config for Xpm if "yes" == @options["xpm"] checking_for("X11/Xpm.h") do ret = false cflags, ldflags, libs = pkg_config("xpm") unless libs.nil? # Update flags @options["cpppath"] << " %s" % [ cflags ] @options["extflags"] << " %s %s" % [ ldflags, libs ] $defs.push("-DHAVE_X11_XPM_H") ret = true else @options["xpm"] = "no" end ret end end # Check pkg-config for Xft if "yes" == @options["xft"] checking_for("X11/Xft/Xft.h") do ret = false cflags, ldflags, libs = pkg_config("xft") unless libs.nil? # Update flags @options["cpppath"] << " %s" % [ cflags ] @options["ldflags"] << " %s %s" % [ ldflags, libs ] @options["extflags"] << " %s %s" % [ ldflags, libs ] $defs.push("-DHAVE_X11_XFT_XFT_H") ret = true else @options["xft"] = "no" end ret end end # Xinerama if "yes" == @options["xinerama"] if have_header("X11/extensions/Xinerama.h") @options["ldflags"] << " -lXinerama" @options["extflags"] << " -lXinerama" end end # Check Xrandr if "yes" == @options["xrandr"] ret = false # Pkg-config checking_for("X11/extensions/Xrandr.h") do cflags, ldflags, libs = pkg_config("xrandr") unless libs.nil? # Version check (xrandr >= 1.3) if try_func("XRRGetScreenResourcesCurrent", libs) # Update flags @options["cflags"] << " %s" % [ cflags ] @options["ldflags"] << " %s %s" % [ ldflags, libs ] $defs.push("-DHAVE_X11_EXTENSIONS_XRANDR_H") ret = true else puts "XRRGetScreenResourcesCurrent couldn't be found" end end @options["xrandr"] = "no" unless ret ret end end # Xtest if "yes" == @options["xtest"] ret = false checking_for("X11/extensions/XTest.h") do # Check for debian header/lib separation if try_func("XTestFakeKeyEvent", "-lXtst") @options["extflags"] << " -lXtst" $defs.push("-DHAVE_X11_EXTENSIONS_XTEST_H") ret = true else puts "XTestFakeKeyEvent couldn't be found" end @options["xtest"] = "no" unless ret ret end end # Check functions FUNCS.each do |f| fail("Func #{f} was not found") unless have_func(f) end # Encoding have_func("rb_enc_set_default_internal") # Defines @defines.each do |k, v| $defs.push(format('-D%s="%s"', k, v)) end # Write files make_config make_header # Dump info puts < [:config, PG_SUBTLE, PG_SUBTLEXT]) # }}} ## subtle {{{ # Wrapper task for subtle ## desc("Build subtle") task(PG_SUBTLE => [:config]) # }}} ## subtlext # {{{ # Wrapper task for subtlext ## desc("Build subtlext") task(PG_SUBTLEXT => [:config]) # }}} ## install {{{ # Install subtle and components ## desc("Install subtle") task(:install => [:config, :build]) do verbose = (true == RakeFileUtils.verbose) # Make install dirs FileUtils.mkdir_p( [ @options["bindir"], @options["configdir"], @options["extdir"], @options["mandir"], File.join(@options["extdir"], "subtler"), File.join(@options["extdir"], "sur") ] ) # Install config message("INSTALL config\n") FileUtils.install( File.join("data", @defines["PKG_CONFIG"]), @options["configdir"], :mode => 0644, :verbose => verbose ) # Install subtle message("INSTALL %s\n" % [PG_SUBTLE]) FileUtils.install( PG_SUBTLE, @options["bindir"], :mode => 0755, :verbose => verbose ) # Install subtlext message("INSTALL %s\n" % [PG_SUBTLEXT]) FileUtils.install( PG_SUBTLEXT + ".so", @options["extdir"], :mode => 0644, :verbose => verbose ) # Get path of sed and ruby interpreter sed = find_executable0("sed") interpreter = File.join( RbConfig.expand(RbConfig::CONFIG["bindir"]), RbConfig::CONFIG["ruby_install_name"] ) # Install subtler message("INSTALL subtler\n") FileList["data/subtler/*.rb"].collect do |f| FileUtils.install(f, File.join(@options["extdir"], "subtler"), :mode => 0644, :verbose => verbose ) end # Install sur message("INSTALL sur\n") FileList["data/sur/*.rb"].collect do |f| FileUtils.install(f, File.join(@options["extdir"], "sur"), :mode => 0644, :verbose => verbose ) end # Install tools message("INSTALL tools\n") FileList["data/bin/*"].collect do |f| FileUtils.install(f, @options["bindir"], :mode => 0755, :verbose => verbose ) # Update interpreter name `#{sed} -i -e 's#/usr/bin/ruby.*##{interpreter}#' \ #{File.join(@options["bindir"], File.basename(f))}` end # Install manpages message("INSTALL manpages\n") FileList["data/man/*.*"].collect do |f| FileUtils.install(f, @options["mandir"], :mode => 0644, :verbose => verbose ) end end # }}} ## uninstall {{{ # Uninstall subtle and components ## desc("Uninstall subtle") task(:uninstall => [:config]) do verbose = (:default != RakeFileUtils.verbose) # Uninstall config message("UNINSTALL config\n") FileUtils.rm_r( @options["configdir"], :verbose => verbose ) # Uninstall subtle message("UNINSTALL %s\n" % [PG_SUBTLE]) FileUtils.rm( File.join(@options["bindir"], PG_SUBTLE), :verbose => verbose ) # Uninstall subtlext message("UNINSTALL %s\n" % [PG_SUBTLEXT]) FileUtils.rm( File.join(@options["extdir"], PG_SUBTLEXT + ".so"), :verbose => verbose ) # Uninstall subtler message("UNINSTALL subtler\n") FileUtils.rm_r( File.join(@options["extdir"], "subtler"), :verbose => verbose ) # Uninstall sur message("UNINSTALL sur\n") FileUtils.rm_r( File.join(@options["extdir"], "sur"), :verbose => verbose ) # Uninstall tools message("UNINSTALL tools\n") FileList["data/bin/*"].collect do |f| FileUtils.rm( File.join(@options["bindir"], File.basename(f)), :verbose => verbose ) end # Install manpages message("UNINSTALL manpages\n") FileList["data/man/*.*"].collect do |f| FileUtils.rm( File.join(@options["mandir"], File.basename(f)), :verbose => verbose ) end end # }}} ## help {{{ # Display help ## desc("Show help") task(:help => [:config]) do puts < src) do compile(src, out, "-D#{PG_SUBTLE.upcase}") end end file(PG_SUBTLE => OBJ_SUBTLE) do silent_sh("#{@options["cc"]} -o #{PG_SUBTLE} #{OBJ_SUBTLE} #{@options["ldflags"]}", "LD #{PG_SUBTLE}") do |ok, status| ok or fail("Linker failed with status #{status.exitstatus}") end end # }}} # subtlext # {{{ SRC_SUBTLEXT.each do |src| out = File.join(@options["builddir"], PG_SUBTLEXT, File.basename(src).ext("o")) file(out => src) do compile(src, out, "-D#{PG_SUBTLEXT.upcase} -fPIC") end end file(PG_SUBTLEXT => OBJ_SUBTLEXT) do silent_sh("#{@options["cc"]} -o #{PG_SUBTLEXT}.so #{OBJ_SUBTLEXT} -shared #{@options["extflags"]}", "LD #{PG_SUBTLEXT}") do |ok, status| ok or fail("Linker failed with status #{status.exitstatus}") end end # }}} # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/data/0000755000175000017500000000000011770527221015125 5ustar formorerformorersubtle-0.11.3224-xi/data/man/0000755000175000017500000000000011770527221015700 5ustar formorerformorersubtle-0.11.3224-xi/data/man/subtle.10000644000175000017500000000762011770332063017263 0ustar formorerformorer.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "SUBTLE" "1" "October 2011" "" "" . .SH "NAME" \fBsubtle\fR \- a grid\-based manual tiling window manager . .SH "SYNOPSIS" \fBsubtle\fR \fIOPTIONS\fR . .SH "DESCRIPTION" \fBsubtle\fR is a \fBmanual\fR tiling window manager with a rather uncommon approach of tiling: Instead of relying on predefined layouts, subtle divides the screen into a grid with customizeable slots (called gravities)\. For better understanding, the default config uses a 3x3 grid and maps each gravity to one key of the numpad\. With those keys, windows can be moved directly to the desired gravity \- the same can be done with tagging rules in the config\. . .P Another unique concept is the \fBstrict\fR tagging: Unlike other tiling window managers, subtle doesn\'t allow \fBweak\fR tagging and always maps windows to virtual desktops (called views) with matching tags, \fBregardless\fR of the current active view\. . .SH "OPTIONS" . .IP "\(bu" 4 \fB\-c\fR, \fB\-\-config\fR=CONFIG . .br Load config . .IP "\(bu" 4 \fB\-d\fR, \fB\-\-display\fR=DISPLAY . .br Connect to DISPLAY . .IP "\(bu" 4 \fB\-h\fR, \fB\-\-help\fR . .br Show this help and exit . .IP "\(bu" 4 \fB\-k\fR, \fB\-\-check\fR . .br Check config syntax . .IP "\(bu" 4 \fB\-n\fR, \fB\-\-no\-randr\fR . .br Disable RandR extension (required for Twinview) . .IP "\(bu" 4 \fB\-r\fR, \fB\-\-replace\fR . .br Replace current window manager . .IP "\(bu" 4 \fB\-s\fR, \fB\-\-sublets\fR=DIR . .br Load sublets from DIR . .IP "\(bu" 4 \fB\-v\fR, \fB\-\-version\fR . .br Show version info and exit . .IP "\(bu" 4 \fB\-l\fR, \fB\-\-level\fR=LEVEL[,LEVEL] Set logging levels . .IP "\(bu" 4 \fB\-D\fR, \fB\-\-debug\fR . .br Print debugging messages . .IP "" 0 . .SH "LOGLEVELS" \fBsubtle\fR uses a loglevel system to distinguish between messages and to be able to turn certain messages off\. The level can be set with the \-l or \-\-level= options, multiple values are comma separated without any whitespace\. . .IP "\(bu" 4 \fBsubtle \-l ruby,subtlext\fR . .IP "\(bu" 4 \fBsubtle \-\-level=ruby,subtlext\fR . .IP "" 0 . .P Default levels: . .IP "\(bu" 4 \fBwarnings\fR Warning messages . .IP "\(bu" 4 \fBsublet\fR Sublet warnings . .IP "\(bu" 4 \fBerror\fR Error messages . .IP "\(bu" 4 \fBdeprecated\fR Deprecation warnings . .IP "" 0 . .P Debugging levels: . .IP "\(bu" 4 \fBevents\fR Information about handled events . .IP "\(bu" 4 \fBruby\fR Ruby debugging messages . .IP "\(bu" 4 \fBxerror\fR X11 error messages . .IP "\(bu" 4 \fBsubtlext\fR Debugging messages of subtlext . .IP "\(bu" 4 \fBsubtle\fR Subsystem messages of subtle . .IP "\(bu" 4 \fBdebug\fR General debugging messages . .IP "" 0 . .SH "GETTING STARTED" To get started with subtle just follow the install instructions, have a look in the \fBINSTALL\fR file in the tarball or check if there is a package for your distribution\. If no package is available and you want to supply one you are welcome\. . .P Next step after installing is configuring\. \fBsubtle\fR follows the \fBXDG\fR specifications, therefore can the default system config be found in \fI$XDG_CONFIG_DIRS/subtle\fR or in \fI/etc/xdg/subtle\fR\. If you prefer a user based config just copy the system config to \fI$XDG_CONFIG_HOME/subtle\fR or to \fI$HOME/\.config/subtle\fR\. . .P If you update the config please check it with \fBsubtle \-k\fR first before starting subtle\. . .P Inside of \fBsubtle\fR just press \fBWin+Return\fR to launch a xterm which usually starts at center gravity\. Once you have a client you can play with gravites to get used to the system\. It\'s really easy and straight forward, just try the various combinations of \fBWin+Numpad\fR number\. . .SH "BUGS" Report bugs at http://subforge\.org/projects/subtle/issues . .br Homepage: http://subtle\.subforge\.org . .SH "COPYRIGHT" Copyright (c) Christoph Kappel \fIunexist@subforge\.org\fR . .SH "SEE ALSO" subtle(1), subtler(1), subtlext(1), sur(1), surserver(1) subtle-0.11.3224-xi/data/man/subtler.10000644000175000017500000002131511770332063017442 0ustar formorerformorer.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "SUBTLER" "1" "November 2011" "" "" . .SH "NAME" \fBsubtler\fR \- a commandline interface for subtle . .SH "SYNOPSIS" \fBsubtler\fR [GENERIC|MODIFIER] GROUP ACTION [ARG1] [ARG2] . .SH "DESCRIPTION" \fBsubtler\fR is a commandline tool, that allows interacting with a \fBEWMH/NetWM\fR compatible window manager and especially with subtle(1) from a shell\. . .P The arguments of \fBsubtler\fR are quite complex and allow a variety of spellings and different number of arguments\. Most of the commands require at least one argument and return output of following format: . .SH "FORMAT" . .SH "INPUT" . .IP "\(bu" 4 \fBDISPLAY\fR : . .IP "\(bu" 4 \fBID\fR . .IP "\(bu" 4 \fBGEOMETRY\fR x++ . .IP "\(bu" 4 \fBNAME\fR . .IP "\(bu" 4 \fBDATA\fR . .IP "\(bu" 4 \fBPATTERN\fR . .br Matching works either via plaintext, regex(7), id or window id if applicable\. If a pattern matches more than once ALL matches are used\. . .IP If the \fBPATTERN\fR is \- subtler will read from stdin\. . .IP "" 0 . .SH "OUTPUT" . .IP "\(bu" 4 \fBClient listing\fR () . .IP "\(bu" 4 \fBGravity listing\fR . .IP "\(bu" 4 \fBScreen listing\fR . .IP "\(bu" 4 \fBTag listing\fR . .IP "\(bu" 4 \fBTray listing\fR () . .IP "\(bu" 4 \fBView listing\fR . .IP "" 0 . .SH "FIELDS" . .IP "\(bu" 4 Numeric (hex) id (e\.g\. 0xa00009) . .IP "\(bu" 4 \- = not visible, * = visible . .IP "\(bu" 4 Numeric id (e\.g\. 5) . .IP "\(bu" 4 x x y + width + height . .IP "\(bu" 4 \- = not set, + = fullscreen, ^ = float, * = stick, ~ = resize, = = zaphod, ! = fixed . .IP "\(bu" 4 Window instance/resource name . .IP "\(bu" 4 Window class name . .IP "\(bu" 4 Numeric id of gravity (e\.g\. 2) . .IP "\(bu" 4 Numeric id of a screen (e\.g\. 1) . .IP "\(bu" 4 Name of a tag (e\.g\. terms) . .IP "" 0 . .SH "OPTIONS" . .SH "GENERIC" . .IP "\(bu" 4 \fB\-d\fR, \fB\-\-display\fR=DISPLAY . .br Connect to DISPLAY (default: :0) . .IP "\(bu" 4 \fB\-h\fR, \fB\-\-help\fR . .br Show this help and exit . .IP "\(bu" 4 \fB\-V\fR, \fB\-\-version\fR . .br Show version info and exit . .IP "\(bu" 4 \fB\-p\fR, \fB\-\-proc\fR . .br This creates a ruby proc from the given argument and yields the result of the group to it as parameter \fIparam\fR\. . .IP \fIExamples\fR: . .IP subtler \-cl \-p "puts param\.name" . .br subtler \-cC \-p "param\.gravity = { terms: :left }" . .IP "" 0 . .SH "MODIFIER" . .IP "\(bu" 4 \fB\-r\fR, \fB\-\-reload\fR . .br Reload subtle . .IP "\(bu" 4 \fB\-R\fR, \fB\-\-restart\fR . .br Restart subtle . .IP "\(bu" 4 \fB\-q\fR, \fB\-\-quit\fR . .br Quit subtle . .IP "\(bu" 4 \fB\-C\fR, \fB\-\-current\fR . .br Select current active window/view instead of passing it via argument\. . .IP \fIExample\fR: subtler \-cCf . .IP "\(bu" 4 \fB\-X\fR, \fB\-\-select\fR . .br Select a window via pointer instead of passing it via argument\. . .IP \fIExample\fR: subtler \-cXf . .IP "" 0 . .SH "GROUPS" . .IP "\(bu" 4 \fB\-c\fR, \fB\-\-Client\fR . .br Use client group . .IP "\(bu" 4 \fB\-g\fR, \fB\-\-Gravity\fR . .br Use gravity group . .IP "\(bu" 4 \fB\-e\fR, \fB\-\-Screen\fR . .br Use screen group . .IP "\(bu" 4 \fB\-s\fR, \fB\-\-Sublet\fR . .br Use sublet group . .IP "\(bu" 4 \fB\-t\fR, \fB\-\-Tag\fR . .br Use tag group . .IP "\(bu" 4 \fB\-y\fR, \fB\-\-Tray\fR . .br Use tray group . .IP "\(bu" 4 \fB\-v\fR, \fB\-\-View\fR . .br Use views group . .IP "" 0 . .SH "ACTIONS FOR CLIENTS" . .IP "\(bu" 4 \fB\-f\fR, \fB\-\-find\fR => PATTERN . .br Find client . .IP \fIExample\fR: subtler \-cf urxvt . .IP "\(bu" 4 \fB\-o\fR, \fB\-\-focus\fR => PATTERN . .br Set focus to client . .IP \fIExample\fR: subtler \-co urxvt . .IP "\(bu" 4 \fB\-F\fR, \fB\-\-full\fR => PATTERN . .br Toggle full . .IP \fIExample\fR: subtler \-cF urxvt . .IP "\(bu" 4 \fB\-O\fR, \fB\-\-float\fR => PATTERN . .br Toggle float . .IP \fIExample\fR: subtler \-cO urxvt . .IP "\(bu" 4 \fB\-S\fR, \fB\-\-stick\fR => PATTERN . .br Toggle stick . .IP \fIExample\fR: subtler \-cS urxvt . .IP "\(bu" 4 \fB\-N\fR, \fB\-\-urgent\fR => PATTERN . .br Toggle urgent . .IP \fIExample\fR: subtler \-cN urxvt . .IP "\(bu" 4 \fB\-l\fR, \fB\-\-list\fR . .br List all clients . .IP "\(bu" 4 \fB\-T\fR, \fB\-\-tag\fR => PATTERN . .br Add tag to client . .IP \fIExample\fR: subtler \-cT urxvt tag . .IP "\(bu" 4 \fB\-U\fR, \fB\-\-untag\fR => PATTERN NAME . .br Remove tag from client . .IP \fIExample\fR: subtler \-cU urxvt tag . .IP "\(bu" 4 \fB\-G\fR, \fB\-\-tags\fR => PATTERN . .br Show client tags . .IP \fIExample\fR: subtler \-cG urxvt . .IP "\(bu" 4 \fB\-Y\fR, \fB\-\-gravity\fR => PATTERN PATTERN . .br Set client gravity . .IP \fIExample\fR: subtler \-cY urxvt gravity . .IP "\(bu" 4 \fB\-E\fR, \fB\-\-raise\fR => PATTERN . .br Raise client window . .IP \fIExample\fR: subtler \-cE urxvt . .IP "\(bu" 4 \fB\-L\fR, \fB\-\-lower\fR => PATTERN . .br Lower client window . .IP \fIExample\fR: subtler \-cL urxvt . .IP "\(bu" 4 \fB\-k\fR, \fB\-\-kill\fR => PATTERN . .br Kill client . .IP \fIExample\fR: subtler \-ck urxvt . .IP "" 0 . .SH "ACTIONS FOR GRAVITIES" . .IP "\(bu" 4 \fB\-a\fR, \fB\-\-add\fR => NAME GEOMETRY . .br Create new gravity . .IP \fIExample\fR: subtler \-ga test 0x0+100+100 . .IP "\(bu" 4 \fB\-l\fR, \fB\-\-list\fR . .br List all gravities . .IP \fIExample\fR: subtler \-gl . .IP "\(bu" 4 \fB\-f\fR, \fB\-\-find\fR => PATTERN . .br Find a gravity . .IP \fIExample\fR: subtler \-gf center . .IP "\(bu" 4 \fB\-k\fR, \fB\-\-kill\fR => PATTERN . .br Kill gravity . .IP \fIExample\fR: subtler \-gk center . .IP "" 0 . .SH "ACTIONS FOR SCREENS" . .IP "\(bu" 4 \fB\-l\fR, \fB\-\-list\fR . .br List all screens . .IP \fIExample\fR: subtler \-el . .IP "\(bu" 4 \fB\-f\fR, \fB\-\-find\fR => ID . .br Find a screen . .IP \fIExample\fR: subtler \-ef 0 . .IP "" 0 . .SH "ACTIONS FOR SUBLETS" . .IP "\(bu" 4 \fB\-l\fR, \fB\-\-list\fR . .br List all sublets . .IP \fIExample\fR: subtler \-sl . .IP "\(bu" 4 \fB\-f\fR, \fB\-\-find\fR => PATTERN . .br Find sublet . .IP \fIExample\fR: subtler \-sf sublet . .IP "\(bu" 4 \fB\-u\fR, \fB\-\-update\fR . .br Updates value of sublet . .IP \fIExample\fR: subtler \-su . .IP "\(bu" 4 \fB\-D\fR, \fB\-\-data\fR => PATTERN DATA . .br Send data to sublet . .IP \fIExample\fR: subtler \-sD sublet something . .IP "\(bu" 4 \fB\-k\fR, \fB\-\-kill\fR => PATTERN . .br Kill sublet . .IP \fIExample\fR: subtler \-sk PATTERN . .IP "" 0 . .SH "ACTIONS FOR TAGS" . .IP "\(bu" 4 \fB\-a\fR, \fB\-\-add\fR => NAME . .br Create new tag . .IP \fIExample\fR: subtler \-ta tag . .IP "\(bu" 4 \fB\-f\fR, \fB\-\-find\fR => PATTERN . .br Find all clients/views by tag . .IP \fIExample\fR: subtler \-ta tag . .IP "\(bu" 4 \fB\-l\fR, \fB\-\-list\fR . .br List all tags . .IP \fIExample\fR: subtler \-tl . .IP "\(bu" 4 \fB\-I\fR, \fB\-\-clients\fR . .br Show clients with tag . .IP \fIExample\fR: subtler \-tI . .IP "\(bu" 4 \fB\-k\fR, \fB\-\-kill\fR => PATTERN . .br Kill tag . .IP \fIExample\fR: subtler \-tk PATTERN . .IP "" 0 . .SH "ACTIONS FOR TRAYS" . .IP "\(bu" 4 \fB\-f\fR, \fB\-\-find\fR => PATTERN . .br Find a tray . .IP \fIExample\fR: subtler \-yf PATTERN . .IP "\(bu" 4 \fB\-l\fR, \fB\-\-list\fR . .br List all trays . .IP \fIExample\fR: subtler \-yl . .IP "\(bu" 4 \fB\-k\fR, \fB\-\-kill\fR => PATTERN . .br Kill tray . .IP \fIExample\fR: subtler \-yk PATTERN . .IP "" 0 . .SH "ACTIONS FOR VIEWS" . .IP "\(bu" 4 \fB\-a\fR, \fB\-\-add\fR => NAME . .br Create new view . .IP \fIExample\fR: subtler \-va NAME . .IP "\(bu" 4 \fB\-f\fR, \fB\-\-find\fR => PATTERN . .br Find a view . .IP \fIExample\fR: subtler \-vf PATTERN . .IP "\(bu" 4 \fB\-l\fR, \fB\-\-list\fR . .br List all views . .IP \fIExample\fR: subtler \-vl . .IP "\(bu" 4 \fB\-T\fR, \fB\-\-tag\fR => PATTERN NAME . .br Add tag to view . .IP \fIExample\fR: subtler \-vT terms tag . .IP "\(bu" 4 \fB\-U\fR, \fB\-\-untag\fR => PATTERN NAME . .br Remove tag from view . .IP \fIExample\fR: subtler \-vT terms tag . .IP "\(bu" 4 \fB\-G\fR, \fB\-\-tags\fR . .br Show view tags . .IP \fIExample\fR: subtler \-vG terms . .IP "\(bu" 4 \fB\-I\fR, \fB\-\-clients\fR . .br Show clients on view . .IP \fIExample\fR: subtler \-vI terms . .IP "\(bu" 4 \fB\-k\fR, \fB\-\-kill\fR => PATTERN . .br Kill view . .IP \fIExample\fR: subtler \-vk terms . .IP "" 0 . .SH "BUGS" Report bugs at http://subforge\.org/projects/subtle/issues . .br Homepage: http://subtle\.subforge\.org . .SH "COPYRIGHT" Copyright (c) Christoph Kappel . .SH "SEE ALSO" subtle(1), subtlext(1), sur(1), surserver(1) subtle-0.11.3224-xi/data/man/sur.10000644000175000017500000001731611770332063016601 0ustar formorerformorer.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "SUR" "1" "October 2011" "" "" . .SH "NAME" \fBsur\fR \- subtle user repository . .SH "SYNOPSIS" \fBsur\fR COMMAND \fIOPTIONS\fR . .SH "DESCIPTION" \fBsur\fR is the \fIsubtle user repository\fR and a manager for sublets in a rubygems like fashion\. . .P The main goal of \fBsur\fR is to help the user managing sublets and to have a central point where new sublets can be found\. Addionally \fBsur\fR has some capabilities to create and test sublets\. . .SH "COMMANDS" . .IP "\(bu" 4 \fBsur\fR \fIannotate\fR NAME [\-v VERSION|\-h] . .br Mark a sublet as to be reviewed . .IP "\(bu" 4 \fBsur\fR \fIbuild\fR SPEC . .br Create a sublet package . .IP "\(bu" 4 \fBsur\fR \fIconfig\fR NAME . .br Show available config settings of a sublet . .IP "\(bu" 4 \fBsur\fR \fIfetch\fR NAME . .br Download sublet to current directory . .IP "\(bu" 4 \fBsur\fR \fIhelp\fR . .br Show this help and exit . .IP "\(bu" 4 \fBsur\fR \fIgrabs\fR NAME . .br Show available grabs provided by a sublet . .IP "\(bu" 4 \fBsur\fR \fIinfo\fR NAME . .br Show info about an installed sublet . .IP "\(bu" 4 \fBsur\fR \fIinstall\fR NAME [\-R|\-t|\-v VERSION|\-h] . .br Install a sublet . .IP "\(bu" 4 \fBsur\fR \fIlist\fR [\-l|\-r|\-h] . .br List local/remote sublets . .IP "\(bu" 4 \fBsur\fR \fInotes\fR NAME . .br Show notes about a sublet . .IP "\(bu" 4 \fBsur\fR \fIquery\fR NAME [\-e|\-l|\-r|\-t|\-v VERSION|\-h] . .br Query for a sublet (e\.g clock, clock \-v 0\.3) . .IP "\(bu" 4 \fBsur\fR \fIreorder\fR . .br Reorder installed sublets for loading order . .IP "\(bu" 4 \fBsur\fR \fIserver\fR [\-p PORT|\-h] . .br Serve sublets (default: http://localhost:4567) . .IP "\(bu" 4 \fBsur\fR \fIsubmit\fR FILE . .br Submit a sublet to SUR . .IP "\(bu" 4 \fBsur\fR \fItemplate\fR FILE . .br Create a new sublet template in current dir . .IP "\(bu" 4 \fBsur\fR \fItest\fR NAME [\-C VALUE|\-h] . .br Test given sublets for syntax and functionality . .IP "\(bu" 4 \fBsur\fR \fIuninstall\fR NAME [\-R|\-t|\-v VERSION|\-h] . .br Uninstall a sublet . .IP "\(bu" 4 \fBsur\fR \fIunpack\fR NAME [\-t|\-v VERSION|\-h] . .br Unpack a sublet in current directory . .IP "\(bu" 4 \fBsur\fR \fIupdate\fR [\-l|\-r|\-h] . .br Update local/remote sublet cache . .IP "\(bu" 4 \fBsur\fR \fIupgrade\fR [\-R|\-y|\-h] . .br Upgrade all installed sublets . .IP "\(bu" 4 \fBsur\fR \fIversion\fR . .br Show version info and exit . .IP "\(bu" 4 \fBsur\fR \fIyank\fR NAME . .br Delete sublet from server . .IP "" 0 . .SH "OPTIONS" . .IP "\(bu" 4 \fBannotate\fR NAME [\-v VERSION|\-h] . .br \fB\-v\fR, \fB\-\-version\fR VERSION Annotate a specific version . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBfetch\fR NAME [\-t|\-v VERSION|\-h] . .br \fB\-t\fR, \fB\-\-tags\fR Include tags in search . .br \fB\-v\fR, \fB\-\-version\fR VERSION Annotate a specific version . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBinstall\fR NAME [\-R|\-t|\-v VERSION|\-h] . .br \fB\-R\fR, \fB\-\-reload\fR Reload sublets after installing . .br \fB\-t\fR, \fB\-\-tags\fR Include tags in search . .br \fB\-v\fR, \fB\-\-version\fR VERSION Search for a specific version . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBlist\fR [\-l|\-r|\-h] . .br \fB\-l\fR, \fB\-\-local\fR Select local repository (default) . .br \fB\-r\fR, \fB\-\-remote\fR Select remote repository . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBquery\fR NAME [\-e|\-l|\-r|\-t|\-v VERSION|\-h] . .br \fB\-e\fR, \fB\-\-regex\fR Use regex for query . .br \fB\-l\fR, \fB\-\-local\fR Select local repository (default) . .br \fB\-r\fR, \fB\-\-remote\fR Select remote repository . .br \fB\-t\fR, \fB\-\-tags\fR Include tags in search . .br \fB\-v\fR, \fB\-\-version\fR VERSION Search for a specific version . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBserver\fR [\-p PORT|\-h] . .br \fB\-p\fR, \fB\-\-port\fR Select a specific port . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBtest\fR NAME [\-C VALUE|\-h] . .br \fB\-C\fR, \fB\-\-config VALUE\fR Add config value (can be used multiple times) . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBunpack\fR NAME [\-t|\-v VERSION|\-h] . .br \fB\-t\fR, \fB\-\-tag\fR Search for a specific tag . .br \fB\-v\fR, \fB\-\-version\fR VERSION Search for a specific version . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBuninstall\fR NAME [\-R|\-t|\-v VERSION|\-h] . .br \fB\-R\fR, \fB\-\-reload\fR Reload sublets after installing . .br \fB\-t\fR, \fB\-\-tag\fR Search for a specific tag . .br \fB\-v\fR, \fB\-\-version\fR VERSION Search for a specific version . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBupdate\fR [\-l|\-r|\-h] . .br \fB\-l\fR, \fB\-\-local\fR Select local repository (default) . .br \fB\-r\fR, \fB\-\-remote\fR Select remote repository . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "\(bu" 4 \fBupgrade\fR [\-R|\-y|\-h] . .br \fB\-R\fR, \fB\-\-reload\fR Reload sublets after upgrading . .br \fB\-y\fR, \fB\-\-yes\fR Assume yes to questions . .br \fB\-h\fR, \fB\-\-help\fR Show this help and exit . .IP "" 0 . .SH "EXAMPLES" . .nf sur install clock sur query \-r clock sur uninstall \-v 0\.1 clock . .fi . .SH "SPECIFICATION" In order to create a sublet, you need to create a specifiction which basically contains some information about your sublet\. . .P Here is a list of known attributes: . .IP "\(bu" 4 \fBname\fR . .br Name of the sublet . .IP \fBExample:\fR spec\.name = "Sublet" . .IP "\(bu" 4 \fBversion\fR . .br Version of the sublet . .IP \fBExample:\fR spec\.version = "0\.1" . .IP "\(bu" 4 \fBtags\fR . .br List of tags to categorize the sublet . .IP \fBExample:\fR spec\.tags = [ "Broken" ] . .IP "\(bu" 4 \fBfiles\fR . .br List of files in the sublet . .IP \fBExample:\fR spec\.files = [ "sublet\.rb" ] . .IP "\(bu" 4 \fBicons\fR . .br List of supplied icons . .IP \fBExample:\fR spec\.icons = [ "icon\.xbm" ] . .IP "\(bu" 4 \fBdescription\fR . .br Description of the sublet . .IP \fBExample:\fR spec\.description = "A shiny new sublet" . .IP "\(bu" 4 \fBnotes\fR . .br Longer description of the sublet . .IP \fBExample:\fR spec\.notes = < "format_string", :type => "string", :description => "Format of the clock (man date)" }, :def_value => "Default value" . .fi . .IP "" 0 . .IP } . .br ] . .IP "\(bu" 4 \fBgrabs\fR . .br Description of grabs . .IP \fBExample:\fR spec\.grabs = [ . .br { . .IP "" 4 . .nf :SubletTest => "Test grab", . .fi . .IP "" 0 . .IP } . .br ] . .IP "\(bu" 4 \fBrequired_version\fR . .br Required version of subtle . .IP \fBExample:\fR spec\.required_version = "0\.9\.10" . .IP "\(bu" 4 \fBadd_dependency(name, version)\fR . .br Add a gem dependency . .IP \fBExample\fR: spec\.add_dependency("a_gem", "0\.0") . .IP "" 0 . .P If you use the \fBtemplate\fR command \fBsur\fR will create an empty template with a spec file\. . .SH "BUGS" Report bugs at http://subforge\.org/projects/subtle/issues . .br Homepage: http://subtle\.subforge\.org . .SH "COPYRIGHT" Copyright (c) Christoph Kappel \fIunexist@dorfelite\.net\fR . .SH "SEE ALSO" surserver(1), subtle(1), subtler(1), subtlext(1) subtle-0.11.3224-xi/data/man/surserver.10000644000175000017500000000121511770332063020017 0ustar formorerformorer.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "SURSERVER" "1" "October 2011" "" "" . .SH "NAME" \fBsurserver\fR \- subtle user repository . .SH "SYNOPSIS" \fBsurserver\fR . .SH "DESCIPTION" \fBsurserver\fR is the server part of \fBsur\fR, it maintains a repository and allows access to it via http on default port 4567\. . .SH "EXAMPLES" . .nf surserver . .fi . .SH "BUGS" Report bugs at http://subforge\.org/projects/subtle/issues . .br Homepage: http://subtle\.subforge\.org . .SH "COPYRIGHT" Copyright (c) Christoph Kappel \fIunexist@subforge\.org\fR . .SH "SEE ALSO" sur(1), subtle(1), subtler(1), subtlext(1) subtle-0.11.3224-xi/data/man/subtlext.10000644000175000017500000000345111770332063017635 0ustar formorerformorer.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "SUBTLEXT" "1" "October 2011" "" "" . .SH "NAME" \fBsubtlext\fR \- a ruby extension for subtle . .SH "SYNOPSIS" \fBsubtlext\fR . .SH "DESCRIPTION" \fBsubtlext\fR is an extension that brings the power of \fBsubtler\fR to Ruby\. . .P A compending list of the classes with it\'s functionality can be found in rdoc and informations about the available unit tests in the development section\. . .SH "EXAMPLE" . .nf require("subtle/subtlext") puts "subtle %s on %s" % [Subtlext::Subtle\.version, Subtlext::Subtle\.running? ? Subtlext::Subtle\.display : "none"] puts "Tags: %s" % [Subtlext::Tag[:all]\.join(", ")] # Views views = [] Subtlext::View[:all]\.each do |v| views\.push("%s (%s)" % [v\.current? ? "[#{v}]" : v, v\.tags\.join(", ")]) end puts "Views: %s" % [views\.join(", ")] # Clients clients = [] Subtlext::Client[:all]\.each do |c| clients\.push("%s (%s)" % [c, c\.tags\.join(", ")]) end puts "Clients: %s" % [clients\.join(", ")] . .fi . .SH "OUTPUT" . .nf subtle 0\.8\.1684 on :0\.0 Tags: default, test, void, terms, browser, editor, stick, float, eight, two, seven, one, bashrun, sakura, python Views: terms (terms, eight, two), [www] (browser, eight, two), void (default, void, eight, two), editor (test, editor, seven, one) Clients: urxvt2 (two, one), urxvt1 (eight, seven), subtle \- Subtlext \- Redmine \- Vimperator (browser), Xephyr on :2\.0 (ctrl+shift grabs mouse and keyboard) (test, float), event\.c (~/projects/subtle/src/subtle) \- GVIM (editor) . .fi . .SH "BUGS" Report bugs at http://subforge\.org/projects/subtle/issues . .br Homepage: http://subtle\.subforge\.org . .SH "COPYRIGHT" Copyright (c) Christoph Kappel \fIunexist@subforge\.org\fR . .SH "SEE ALSO" subtle(1), subtler(1), sur(1), surserver(1) subtle-0.11.3224-xi/data/sur/0000755000175000017500000000000011770527221015736 5ustar formorerformorersubtle-0.11.3224-xi/data/sur/specification.rb0000644000175000017500000002621111770332063021103 0ustar formorerformorer# -*- encoding: utf-8 -*- # # @package sur # # @file Specification functions # @author Christoph Kappel # @version $Id: data/sur/specification.rb,v 3182 2012/02/04 16:39:33 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # require "archive/tar/minitar" require "rubygems" module Subtle # {{{ module Sur # {{{ # Specification class for specs class Specification # {{{ # Sublet name attr_accessor :name # Sublet version attr_accessor :version # Tag list attr_accessor :tags # File list attr_accessor :files # Icon list attr_accessor :icons # Short description attr_accessor :description # Notes attr_accessor :notes # Author list attr_accessor :authors # Contact address attr_accessor :contact # Date of creation attr_accessor :date # Config values attr_accessor :config # Provided grabs attr_accessor :grabs # Hash of dependencies attr_accessor :dependencies # Version of subtle attr_accessor :required_version # Path of spec attr_accessor :path # Digest attr_accessor :digest ## Subtle::Sur::Specification::initialize {{{ # Create a new Specification object # # @yield [self] Init block # @yieldparam [Object] self Self instance # @return [Object] New Specification # # @since 0.1 # # @example # Subtle::Sur::Specification.new # => # def initialize @name = "Unnamed" @authors = [] @dependencies = {} @subtlext_version = nil @sur_version = nil @files = [] @icons = [] @path = Dir.pwd # Pass to block yield(self) if block_given? end # }}} ## Subtle::Sur::Specification::load_spec {{{ # Load Specification from file # # @param [String] file Specification file name # @return [Object] New Specification # # @raise [String] Loading error # @since 0.1 # # @example # Subtle::Sur::Specification.load_file("sublet.spec") # => # def self.load_spec(file) spec = nil # Catch IO exceptions begin # Create spec spec = eval(File.open(file).read) spec.path = File.dirname(file) rescue Exception => e spec = nil end # Check object if spec.nil? or !spec.is_a?(Specification) raise "Cannot read specification file `#{file}'" elsif !spec.valid? raise spec.validate end spec end # }}} ## Subtle::Sur::Specification::extract_spec {{{ # Extract and load Specification from file # # @param [String] file Tar file name # @return [Object] New Specification # # @raise [String] Loading error # @see Subtle::Sur::Specification.load_spec # @since 0.1 # # @example # Subtle::Sur::Specification.extract_spec("foo.sublet") # => # def self.extract_spec(file) spec = nil begin File.open(file, "rb") do |input| Archive::Tar::Minitar::Input.open(input) do |tar| tar.each do |entry| if ".spec" == File.extname(entry.full_name) spec = eval(entry.read) end end end end rescue spec = nil end # Check object if spec.nil? or !spec.is_a?(Specification) raise "Cannot extract specification from file `#{file}'" elsif !spec.valid? raise spec.validate end spec end # }}} ## Subtle::Sur::Specification::template {{{ # Create a new Specification object # # @param [String] file Template name # # @since 0.1 # # @example # Subtle::Sur::Specification.template("foobar") # => nil def self.template(file) require "subtle/sur/version" # Build filenname ext = File.extname(file) ext = ".rb" if(ext.empty?) name = File.basename(file, ext) folder = File.join(Dir.pwd, name) spec = File.join(folder, name + ".spec") sublet = File.join(folder, name + ".rb") unless File.exist?(name) FileUtils.mkdir_p([ folder ]) # Create spec File.open(spec, "w+") do |output| output.puts < "Value name", # :type => "Value type", # :description => "Description", # :def_value => "Default value" # } #] # Sublet grabs #s.grabs = { # :#{name.capitalize}Grab => "Sublet grab" #} # Sublet requirements # s.required_version = "0.9.2127" # s.add_dependency("subtle", "~> 0.1.2") end EOF end # Create sublet File.open(sublet, "w+") do |output| output.puts <>> Created template for `#{name}'" else raise "File `%s' already exists" % [ name ] end end # }}} ## Subtle::Sur::Specification::valid? {{{ # Checks if a specification is valid # # @return [true, false] Validity of the package # # @since 0.1 # # @example # Subtle::Sur::Specification.new.valid? # => true def valid? (!@name.nil? and !@authors.empty? and !@contact.nil? and \ !@description.nil? and !@version.nil? and !@files.empty?) end # }}} ## Subtle::Sur::Specification::validate {{{ # Check if a specification is valid # # @raise [String] Validation error # @since 0.1 # # @example # Subtle::Sur::Specification.new.validate # => nil def validate fields = [] # Check required fields of spec fields.push("name") if(@name.nil?) fields.push("authors") if(@authors.empty?) fields.push("contact") if(@contact.nil?) fields.push("description") if(@description.nil?) fields.push("version") if(@version.nil?) fields.push("files") if(@files.empty?) unless fields.empty? raise SpecificationValidationError, "Couldn't find `#{fields.join(", ")}' in specification" end end # }}} ## Subtle::Sur::Specification::add_dependency {{{ # Add a gem dependency to the package # # @param [String] name Dependency name # @param [String] version Required version # # @since 0.1 # # @example # Subtle::Sur::Specification.new.add_dependency("subtle", "0.8") # => nil def add_dependency(name, version) @dependencies[name] = version end # }}} ## Subtle::Sur::Specification::satisfied? {{{ # Check if all dependencies are satisfied # # @return [true, false] Validity of the package # # @since 0.1 # # @example # Subtle::Sur::Specification.new.satisfied? # => true def satisfied? satisfied_version = true satisfied_deps = true missing = [] # Check subtlext version unless @required_version.nil? begin require "subtle/subtlext" # Check subtlext version major_have, minor_have, teeny_have = Subtlext::VERSION.split(".").map(&:to_i) major_need, minor_need, teeny_need = @required_version.split(".").map(&:to_i) if(major_need > major_have or minor_need > minor_have or teeny_need.nil? or teeny_have.nil? or teeny_need > teeny_have) satisfied_version = false end rescue => err puts ">>> ERROR: Couldn't verify version of subtle" satisfied_version = false end end # Check gem dependencies @dependencies.each do |k, v| begin gem(k, v) rescue Gem::LoadError # Try to load it begin require k puts ">>> WARNING: Couldn't verify version of `#{k}' with rubygems" rescue LoadError missing.push("%s (%s)" % [ k, v]) satisfied_deps = false end end end # Dump errors unless satisfied_version or satisfied_deps puts ">>> ERROR: Couldn't install `#{@name}' due to unsatisfied requirements." unless(satisfied_version) puts " Subtle >=#{@required_version} (found: #{Subtlext::VERSION}) is required." end unless(missing.empty?) puts " Following gems are missing: #{missing.join(", ")}" end end satisfied_version and satisfied_deps end # }}} ## Subtle::Sur::Specification::to_str {{{ # Convert Specification to string # # @return [String] Specification as string # # @since 0.1 # # @example # Subtle::Sur::Specification.new.to_str # => "sublet-0.1" def to_str "#{@name}-#{@version}".downcase end # }}} ## Subtle::Sur::Specification::method_missing {{{ # Dispatcher for Specification instance # # @param [Symbol] meth Method name # @param [Array] args Argument array # # @since 0.2 def method_missing(meth, *args) ret = nil # Check if symbol is a method or a var if self.respond_to?(meth) ret = self.send(meth, args) else sym = ("@" + meth.to_s).to_sym #< Construct symbol # Check getter or setter if(meth.to_s.index("=")) sym = sym.to_s.chop.to_sym self.instance_variable_set(sym, args.first) else ret = self.instance_variable_get(sym) ret = self if(ret.nil?) end end ret end # }}} # Aliases alias :to_s :to_str # FIXME: Deprecated alias :subtlext_version :required_version alias :subtlext_version= :required_version= end # }}} end # }}} end # }}} # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/data/sur/runner.rb0000644000175000017500000003242311770332063017576 0ustar formorerformorer# -*- encoding: utf-8 -*- # # @package sur # # @file Runner functions # @author Christoph Kappel # @version $Id: data/sur/runner.rb,v 3182 2012/02/04 16:39:33 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # require "subtle/sur/client" module Subtle # {{{ module Sur # {{{ # Runner class for argument dispatching class Runner # {{{ # Trap SIGINT trap "INT" do exit end ## Subtle::Sur::Runner::run {{{ # Start runner # # @param [Array] args Argument array # # @raise [String] Command error # @since 0.0 # # Subtle::Sur::Runner.new.run(args) # => nil def run(args) # Default options @repo = "local" @version = nil @port = 4567 @use_tags = false @use_color = true @use_regex = false @reload = false @assume = false @config = [] # GetoptLong only works on ARGV so we use something own begin idx = 0 no_opt = [] while(idx < args.size) case args[idx] # Enable flags when "-l", "--local" then @repo = "local" when "-r", "--remote" then @repo = "remote" when "-c", "--no-color" then @use_color = false when "-e", "--regex" then @use_regex = true when "-t", "--tag" then @use_tags = true when "-R", "--reload" then @reload = true when "-y", "--yes" then @assume = true # Get command help when "-h", "--help" usage(args[0]) exit # Get option arguments when "-v", "--version" idx += 1 @version = args[idx] when "-p", "--port" idx += 1 @port = args[idx].to_i when "-C", "--config" idx += 1 @config << args[idx] # Save other arguments else no_opt << args[idx] end idx += 1 end args = no_opt rescue raise "Cannot find required arguments" end # Run cmd = args.shift case cmd when "annotate" usage(cmd) if args.empty? Subtle::Sur::Client.new.annotate(args.first, @version) when "build" usage(cmd) if args.empty? Subtle::Sur::Client.new.build(args.first) when "config" usage(cmd) if args.empty? Subtle::Sur::Client.new.config(args.first, @use_color) when "fetch" usage(cmd) if args.empty? Subtle::Sur::Client.new.fetch(args, @version, @use_tags) when "grabs" usage(cmd) if args.empty? Subtle::Sur::Client.new.grabs(args.first, @use_color) when "help" then usage(nil) when "info" usage(cmd) if args.empty? Sur::Client.new.info(args, @use_color) when "install" usage(cmd) if args.empty? Sur::Client.new.install(args, @version, @use_tags, @reload) when "list" Subtle::Sur::Client.new.list(@repo, @use_color) when "notes" usage(cmd) if args.empty? Subtle::Sur::Client.new.notes(args.first) when "query" usage(cmd) if args.empty? Subtle::Sur::Client.new.query(args.first, @repo, @version, @use_regex, @use_tags, @use_color ) when "reorder" Subtle::Sur::Client.new.reorder when "server" require "subtle/sur/server" Sur::Server.new(@port).run when "submit" usage(cmd) if args.empty? Subtle::Sur::Client.new.submit(args.first) when "template" usage(cmd) if args.empty? Subtle::Sur::Specification.template(args.first) when "test" usage(cmd) if args.empty? require "subtle/sur/test" Subtle::Sur::Test.run(@config, args) when "uninstall" usage(cmd) if args.empty? Subtle::Sur::Client.new.uninstall(args, @version, @use_tags, @reload) when "unpack" Subtle::Sur::Client.new.unpack(args, @version, @use_tags) when "update" Subtle::Sur::Client.new.update(@repo) when "upgrade" Subtle::Sur::Client.new.upgrade(@use_color, @assume, @reload) when "version" then version when "yank" usage(cmd) if args.empty? Subtle::Sur::Client.new.update(args.first, @version) else usage(nil) end end # }}} private def usage(arg) # {{{ case arg when "annotate" puts "Usage: sur annotate NAME [OPTIONS]\n\n" \ "Mark sublet as to be reviewed\n\n" \ "Options:\n" \ " -v, --version VERSION Annotate a specific version\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur annotate -l\n" \ "sur annotate -r\n" when "fetch" puts "Usage: sur fetch NAME [OPTIONS]\n\n" \ "Download sublet to current directory\n\n" \ "Options:\n" \ " -t, --tags Include tags in search\n" \ " -v, --version VERSION Search for a specific version\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur fetch clock\n" \ "sur fetch clock -v 0.3\n" when "install" puts "Usage: sur install NAME [OPTIONS]\n\n" \ "Install a sublet by given name\n\n" \ "Options:\n" \ " -R, --reload Reload sublets after install\n" \ " -t, --tags Include tags in search\n" \ " -v, --version VERSION Search for a specific version\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur install clock\n" \ "sur install clock -v 0.3\n" when "list" puts "Usage: sur list [OPTIONS]\n\n" \ "List sublets in repository\n\n" \ "Options:\n" \ " -l, --local Select local repository (default)\n" \ " -r, --remote Select remote repository\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur list -l\n" \ "sur list -r\n" when "query" puts "Usage: sur query NAME [OPTIONS]\n\n" \ "Query a repository for a sublet by given name\n\n" \ "Options:\n" \ " -e, --regex Use regex for query\n" \ " -l, --local Select local repository (default)\n" \ " -r, --remote Select remote repository\n" \ " -t, --tags Include tags in search\n" \ " -v, --version VERSION Search for a specific version\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur query -l clock\n" \ "sur query -r clock -v 0.3\n" when "server" puts "Usage: sur server [OPTIONS]\n\n" \ "Serve sublets on localhost and optionally on a given port\n\n" \ "Options:\n" \ " -p, --port PORT Select a specific port\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur server -p 3000\n" when "test" puts "Usage: sur test NAME [OPTIONS]\n\n" \ "Test given sublets for syntax and functionality\n\n" \ "Options:\n" \ " -C, --config VALUE Add config value (can be used multiple times)\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur test -C user=name -C pass=pass sublet.rb\n" \ "sur test sublet.rb\n" when "unpack" puts "Usage: sur unpack NAME [OPTIONS]\n\n" \ "Unpack sublet to current path\n\n" \ "Options:\n" \ " -t, --tags Include tags in search\n" \ " -v, --version VERSION Search for a specific version\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur unpack clock\n" \ "sur unpack clock -v 0.3\n" when "uninstall" puts "Usage: sur uninstall NAME [OPTIONS]\n\n" \ "Uninstall a sublet by given name and optionally by given version\n\n" \ "Options:\n" \ " -R, --reload Reload sublets after uninstall\n" \ " -t, --tags Include tags in search\n" \ " -v, --version VERSION Select a specific version\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur uninstall clock\n" \ "sur uninstall clock -v 0.3\n" when "update" puts "Usage: sur update [-l|-r|-h]\n\n" \ "Update local/remote sublet cache\n\n" \ "Options:\n" \ " -l, --local Select local repository (default)\n" \ " -r, --remote Select remote repository\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur update -l\n" \ "sur update -r\n" when "upgrade" puts "Usage: sur upgrade [-R|-h]\n\n" \ "Upgrade all sublets if possible\n\n" \ "Options:\n" \ " -R, --reload Reload sublets after upgrading\n" \ " -y, --yes Assume yes to questions\n" \ " -h, --help Show this help and exit\n\n" \ "Examples:\n" \ "sur upgrade\n" \ "sur upgrade -R\n" else puts "Usage: sur [OPTIONS]\n\n" \ "Options:\n" \ " annotate NAME [-v VERSION|-h] Mark sublet to be reviewed\n" \ " build SPEC Create a sublet package\n" \ " config NAME Show available config settings of a sublet\n" \ " fetch NAME Download sublet to current directory\n" \ " help Show this help and exit\n" \ " grabs NAME Show available grabs provided by a sublet\n" \ " info NAME Show info about an installed sublet\n" \ " install NAME [-R|-t|-v VERSION|-h] Install a sublet\n" \ " list [-l|-r|-h] List local/remote sublets\n" \ " notes NAME Show notes about a sublet\n" \ " query NAME [-e|-l|-t|-v VERSION|-h] Query for a sublet (e.g clock, clock -v 0.3)\n" \ " reorder Reorder installed sublets for loading order\n" \ " server [-p PORT|-h] Serve sublets (default: http://localhost:4567)\n" \ " submit FILE Submit a sublet to SUR\n" \ " template FILE Create a new sublet template in current dir\n" \ " test NAME [-C VALUE|-h] Test sublets for syntax and functionality\n" \ " uninstall NAME [-R|-t|-v VERSION|-h] Uninstall a sublet\n" \ " unpack NAME [-t|-v VERSION|-h] Unpack a sublet in current directory\n" \ " update [-l|-r|-h] Update local/remote sublet cache\n" \ " upgrade [-R|-y|-h] Upgrade all installed sublets\n" \ " version Show version info and exit\n" " yank [-v VERSION] Delete sublet from server\n" end puts "\nPlease report bugs at http://subforge.org/projects/subtle/issues\n" exit end # }}} def version # {{{ puts "Sur #{VERSION} - Copyright (c) 2009-2012 Christoph Kappel\n" \ "Released under the GNU General Public License\n" end # }}} end # }}} end # }}} end # }}} # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/data/sur/client.rb0000644000175000017500000010254611770332063017547 0ustar formorerformorer# -*- encoding: utf-8 -*- # # @package sur # # @file Client functions # @author Christoph Kappel # @version $Id: data/sur/client.rb,v 3182 2012/02/04 16:39:33 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # require "rubygems" require "fileutils" require "tempfile" require "yaml" require "uri" require "net/http" require "archive/tar/minitar" require "subtle/sur/specification" module Subtle # {{{ module Sur # {{{ # Client class for interaction with the user class Client # {{{ # Remote repository host HOST = "http://sur.subforge.org" # Header separator BOUNDARY = "AaB03x" # Local sublet cache attr_accessor :cache_local # Remote sublet cache attr_accessor :cache_remote # Path to local cache file attr_accessor :path_local # Path to remote cache file attr_accessor :path_remote # Path to icons attr_accessor :path_icons # Path of specification attr_accessor :path_specs # Path to sublets attr_accessor :path_sublets ## Sur::Client::initialize {{{ # Create a new Client object # # @return [Object] New Client # # @since 0.0 # # @example # client = Sur::Client.new # => # def initialize # Paths xdg_cache_home = File.join((ENV["XDG_CACHE_HOME"] || File.join(ENV["HOME"], ".cache")), "sur") xdg_data_home = File.join((ENV["XDG_DATA_HOME"] || File.join(ENV["HOME"], ".local", "share")), "subtle") @path_local = File.join(xdg_cache_home, "local.yaml") @path_remote = File.join(xdg_cache_home, "remote.yaml") @path_icons = File.join(xdg_data_home, "icons") @path_specs = File.join(xdg_data_home, "specifications") @path_sublets = File.join(xdg_data_home, "sublets") # Create folders [ xdg_cache_home, xdg_data_home, @path_icons, @path_specs, @path_sublets ].each do |p| FileUtils.mkdir_p([ p ]) unless File.exist?(p) end end # }}} ## Sur::Client::annotate {{{ # Mark a sublet as to be reviewed # # @param [String] name Name of the Sublet # @param [String] version Version of the Sublet # # @raise [String] Annotate error # @since 0.2 # # @example # Sur::Client.new.submit("sublet") # => nil def annotate(name, version = nil) build_local build_remote # Check if sublet exists if (specs = search(name, @cache_remote, version, false)) and specs.empty? raise "Sublet `#{name}' does not exist" end spec = specs.first uri = URI.parse(HOST + "/annotate") res = Net::HTTP.post_form(uri, { "digest" => spec.digest, "user" => ENV["USER"] } ) raise "Cannot annotate sublet: Sublet not found" if 404 == res.code end # }}} ## Sur::Client::build {{{ # Build a sublet package # # @param [String] file Spec file name # # @see Sur::Specification.load_file # @raise [String] Build error # @since 0.1 # # @example # Sur::Client.new.build("sublet.spec") # => nil def build(file) spec = Sur::Specification.load_spec(file) # Check specification if spec.valid? begin sublet = "%s-%s.sublet" % [ File.join(Dir.pwd, spec.name.downcase), spec.version ] opts = { :mode => 644, :mtime => Time.now.to_i } # Check if files exist (spec.files | spec.icons).each do |f| unless File.exist?(File.join(File.dirname(file), f)) raise "Cannot find file `#{f}'" end end # Check gem version unless spec.dependencies.empty? spec.dependencies.each do |name, version| if version.match(/^(\d*\.){1,2}\d*$/) puts ">>> WARNING: Gem dependency `%s' " \ "requires exact gem version (=%s)" % [ name, version ] end end end # Create tar file File.open(sublet, "wb") do |output| Archive::Tar::Minitar::Writer.open(output) do |tar| # Add Spec tar.add_file(File.basename(file), opts) do |os| os.write(IO.read(file)) end # Add files spec.files.each do |f| tar.add_file(File.basename(f), opts) do |os| os.write(IO.read(File.join(File.dirname(file), f))) end end # Add icons spec.icons.each do |f| tar.add_file(File.basename(f), opts) do |os| os.write(IO.read(File.join(File.dirname(file), f))) end end end end puts ">>> Created sublet `#{File.basename(sublet)}'" rescue => error raise error.to_s end else spec.validate end end # }}} ## Sur::Client::config {{{ # Show config settings for installed sublets if any # # @param [String] name Name of the Sublet # @param [Bool] use_color Use colors # # @raise [String] Config error # @since 0.2 # # @example # Sur::Client.new.config("sublet") # => "Name Type Default value Description" # "interval integer 60 Update interval in seconds" def config(name, use_color = true) build_local # Check if sublet is installed if (specs = search(name, @cache_local)) and !specs.empty? spec = specs.first show_config(spec, use_color) see_also(spec) end end # }}} ## Sur::Client::fetch {{{ # Install a new Sublet # # @param [Array] names Names of Sublets # @param [String] version Version of the Sublet # @param [String] use_tags Use tags # # @raise [String] Fetch error # @since 0.0 # # @example # Sur::Client.new.fetch([ "sublet" ]) # => nil def fetch(names, version = nil, use_tags = false) build_remote # Install all sublets names.each do |name| # Check if remote sublet exists if (specs = search(name, @cache_remote, version, use_tags)) and specs.empty? puts ">>> WARNING: Cannot find sublet `#{name}' " \ "in remote repository" next end spec = specs.first # Download and copy sublet to current directory unless (temp = download(spec)).nil? FileUtils.cp(temp.path, File.join( Dir.getwd, "%s-%s.sublet" % [ spec.name.downcase, spec.version ] ) ) end end end # }}} ## Sur::Client::grabs {{{ # Show grabs for installed sublets if any # # @param [String] name Name of the Sublet # @param [Bool] use_color Use colors # # @raise [String] Config error # @since 0.2 # # @example # Sur::Client.new.grabs("sublet") # => "Name Description" # "SubletTest Test grabs" def grabs(name, use_color = true) build_local # Check if sublet is installed if (specs = search(name, @cache_local)) and !specs.empty? spec = specs.first show_grabs(spec, use_color) see_also(spec) end end # }}} ## Sur::Client::info {{{ # Show info for installed sublets if any # # @param [Array] names Names of the Sublets # @param [Bool] use_color Use colors # # @raise [String] Config error # @since 0.2 # # @example # Sur::Client.new.info("sublet") # => "Name Description" # "SubletTest Test grabs" def info(names, use_color = true) build_local # Show info for all sublets names.each do |name| # Check if sublet is installed if (specs = search(name, @cache_local)) and !specs.empty? spec = specs.first show_info(spec, use_color) see_also(spec) end end end # }}} ## Sur::Client::install {{{ # Install a new Sublet # # @param [Array] names Names of Sublets # @param [String] version Version of the Sublet # @param [String] use_tags Use tags # @param [Bool] reload Reload sublets after installing # # @raise [String] Install error # @since 0.0 # # @example # Sur::Client.new.install([ "sublet" ]) # => nil def install(names, version = nil, use_tags = false, reload = false) build_local build_remote # Install all sublets names.each do |name| # Check if sublet is already installed if (specs = search(name, @cache_local, version, use_tags)) and !specs.empty? puts ">>> WARNING: Sublet `#{name}' is already installed" next end # Check if sublet is local if File.exist?(name) install_sublet(name) next end # Check if remote sublet exists if (specs = search(name, @cache_remote, version, use_tags)) and specs.empty? puts ">>> WARNING: Cannot find sublet `#{name}' " \ "in remote repository" next end # Check dependencies spec = specs.first next unless spec.satisfied? # Download and install sublet unless (temp = download(spec)).nil? install_sublet(temp.path) end end build_local(true) reload_sublets if reload end # }}} ## Sur::Client::list {{{ # List the Sublet in a repository # # @param [String] repo Repo name # @param [Bool] use_color Use colors # # @since 0.0 # # @example # Sur::Client.new.list("remote") # => nil def list(repo, use_color = true) # Select cache case repo when "local" build_local specs = @cache_local when "remote" build_local build_remote specs = @cache_remote end show_list(specs, use_color) end # }}} ## Sur::Client::notes {{{ # Show notes about an installed sublet if any # # @param [String] name Name of the Sublet # # @raise [String] Notes error # @since 0.2 # # @example # Sur::Client.new.notes("sublet") # => "Notes" def notes(name) build_local # Check if sublet is installed if (specs = search(name, @cache_local)) and !specs.empty? spec = specs.first show_notes(spec) see_also(spec) end end # }}} ## Sur::Client::query {{{ # Query repo for a Sublet # # @param [String] query Query string # @param [String] repo Repo name # @param [Bool] use_regex Use regex # @param [Bool] use_tags Use tags # @param [Bool] use_color Use colors # # @raise [String] Query error # @since 0.0 # # @example # Sur::Client.new.query("sublet", "remote") # => nil def query(query, repo, version = nil, use_regex = false, use_tags = false, use_color = true) case repo when "local" build_local unless (specs = search(query, @cache_local, version, use_regex, use_tags)) and !specs.empty? raise "Cannot find `#{query}' in local repository" end when "remote" build_local build_remote unless (specs = search(query, @cache_remote, version, use_regex, use_tags)) and !specs.empty? raise "Cannot find `#{query}' in remote repository" end end show_list(specs, use_color) end # }}} ## Sur::Client::reorder {{{ # Reorder install Sublet # # @example # Sur::Client.new.reorder # => nil def reorder build_local i = 0 list = [] files = Dir[@path_sublets + "/*"] # Show menu @cache_local.each do |s| puts "(%d) %s (%s)" % [ i + 1, s.name, s.version ] # Check for match if sublet was reordered files.each do |f| s.files.each do |sf| a = File.basename(f) b = File.basename(sf) if a == b or File.fnmatch("[0-9_]*#{b}", a) list.push([ s.name.downcase, a]) end end end i += 1 end # Get new order puts "\n>>> Enter new numbers separated by blanks:\n" printf ">>> " line = STDIN.readline i = 0 if "\n" != line line.split(" ").each do |tok| idx = tok.to_i name, file = list.at(idx -1) i += 10 new_path = '%s/%d_%s.rb' % [ @path_sublets, i, name ] # Check if file exists before moving unless File.exist?(new_path) FileUtils.mv(File.join(@path_sublets, file), new_path) end end build_local(true) #< Update local cache end end # }}} ## Sur::Client::submit {{{ # Submit a Sublet to a repository # # @param [String] file Sublet package # # @raise [String] Submit error # @since 0.0 # # @example # Sur::Client.new.submit("sublet") # => nil def submit(file) if !file.nil? and File.file?(file) and ".sublet" == File.extname(file) spec = Sur::Specification.extract_spec(file) if spec.valid? upload(file) build_remote(true) else spec.validate end else raise "Cannot find file `#{file}'" end end # }}} ## Sur::Client::uninstall {{{ # Uninstall a Sublet # # @param [Array] names Names of Sublets # @param [String] version Version of the Sublet # @param [Bool] use_tags Use tags # @param [Bool] reload Reload sublets after uninstalling # # @raise [String] Uninstall error # @since 0.0 # # @example # Sur::Client.new.uninstall("sublet") # => nil def uninstall(names, version = nil, use_tags = false, reload = false) build_local # Install all sublets names.each do |name| # Check if sublet is installed if (specs = search(name, @cache_local, version, use_tags)) and !specs.empty? spec = specs.first # Uninstall files spec.files.each do |f| # Check for match if sublet was reordered Dir[@path_sublets + "/*"].each do |file| a = File.basename(f) b = File.basename(file) if a == b or File.fnmatch("[0-9_]*#{a}", b) puts ">>>>>> Uninstalling file `#{b}'" FileUtils.remove_file(File.join(@path_sublets, b), true) end end end # Uninstall icons spec.icons.each do |f| puts ">>>>>> Uninstalling icon `#{f}'" FileUtils.remove_file( File.join(@path_icons, File.basename(f)), true ) end # Uninstall specification puts ">>>>>> Uninstalling specification `#{spec.to_s}.spec'" FileUtils.remove_file( File.join(@path_specs, spec.to_s + ".spec"), true ) puts ">>> Uninstalled sublet #{spec.to_s}" else puts ">>> WARNING: Cannot find sublet `#{name}' in local " \ "repository" end end build_local(true) reload_sublets if reload end # }}} ## Sur::Client::unpack {{{ # Unpack sublet to current path # # @param [Array] names Names of Sublets # @param [String] version Version of the Sublet # @param [Bool] use_tags Use tags # # @raise [String] Unpack error # @since 0.0 # # @example # Sur::Client.new.unpack("sublet") # => nil def unpack(names, version = nil, use_tags = false) build_remote # Install all sublets names.each do |name| # Check if sublet is installed if (specs = search(name, @cache_remote, version, use_tags)) and !specs.empty? spec = specs.first # Download and unpack sublet unless (temp = download(spec)).nil? base = File.join(Dir.pwd, spec.to_str) icons = File.join(base, "icons") FileUtils.mkdir_p([ icons ]) install_sublet(temp.path, base, icons, base) end end end build_local(true) end # }}} ## Sur::Client::update {{{ # Update a repository # # @param [String] repo Repo name # # @since 0.0 # # @example # Sur::Client.new.update("remote") # => nil def update(repo) case repo when "local" build_local(true) when "remote" build_remote(true) end end # }}} ## Sur::Client::upgrade {{{ # Upgrade all Sublets # # @param [Bool] use_color Use colors # @param [Bool] reload Reload sublets after uninstalling # @param [Bool] assume Whether to assume yes # # @raise Upgrade error # @since 0.2 # # @example # Sur::Client.new.upgrade # => nil def upgrade(use_color = true, reload = false, assume = false) build_local build_remote list = [] # Iterate over server-side sorted list @cache_local.each do |spec_a| @cache_remote.each do |spec_b| if spec_a.name == spec_b.name and spec_a.version.to_f < spec_b.version.to_f list << spec_b.name if use_color puts ">>> %s: %s -> %s" % [ spec_a.name, colorize(5, spec_a.version), colorize(2, spec_b.version) ] else puts ">>> %s: %s -> %s" % [ spec_a.name, spec_a.version, spec_b.version ] end break end end end return if list.empty? # Really? unless assume print ">>> Upgrade sublets (y/n)? " return unless "y" == STDIN.readline.strip.downcase end # Finally upgrade uninstall(list) install(list) reload_sublets if reload end # }}} ## Sur::Client::yank {{{ # Delete a sublet from remote server # # @param [String] name Name of the Sublet # @param [String] version Version of the Sublet # # @raise Upgrade error # @since 0.2 # # @example # Sur::Client.new.yank("subtle", "0.0") # => nil def yank(use_color = true, reload = false, assume = false) raise NotImplementedError end # }}} private def see_also(spec) # {{{ also = [ "info", "config" ] also << "notes" unless spec.notes.nil? also << "grabs" unless spec.grabs.nil? puts "See also: #{also.join(", ")}" end # }}} def upload(file) # {{{ uri = URI.parse(HOST + "/submit") http = Net::HTTP.new(uri.host, uri.port) base = File.basename(file) body = "" # Assemble data body << "--#{BOUNDARY}\r\n" body << "Content-Disposition: form-data; name=\"user\"\r\n\r\n" body << ENV["USER"] body << "\r\n--#{BOUNDARY}\r\n" body << "Content-Disposition: form-data; name=\"file\"; filename=\"#{base}\"\r\n" body << "Content-Type: application/x-tar\r\n\r\n" body << File.read(file) body << "\r\n--#{BOUNDARY}--\r\n" # Send reqiest req = Net::HTTP::Post.new(uri.request_uri) req.body = body req["Content-Type"] = "multipart/form-data; boundary=#{BOUNDARY}" # Check result case http.request(req).code.to_i when 200 puts ">>> Submitted sublet `#{base}'" build_remote when 405 then raise "Cannot submit sublet: Invalid request" when 415 then raise "Cannot submit sublet: Invalid Spec" when 500 then raise "Cannot submit sublet: Server error" else raise "Cannot submit sublet" end end # }}} def download(spec) # {{{ temp = nil uri = URI.parse(HOST) # Proxy? begin proxy = URI.parse(ENV["HTTP_PROXY"]) http = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password).start(uri.host, uri.port) rescue http = Net::HTTP.new(uri.host, uri.port) end # Fetch file http.request_get("/get/" + spec.digest) do |response| # Check result case response.code.to_i when 200 total = 0 # Create tempfile temp = Tempfile.new(spec.to_s) # Write file content response.read_body do |str| total += str.length temp << str progress(">>> Fetching sublet `#{spec.to_s}'", response.content_length, total) end temp.close when 404 then raise "Cannot download sublet: Sublet not found" else raise "Cannot download sublet: Server error" end end puts return temp end # }}} def progress(mesg, total, now) # {{{ if 0.0 < total percent = (now.to_f * 100 / total.to_f).floor $stdout.sync = true print "\r#{mesg} (#{percent}%)" end end # }}} def search(query, repo, version = nil, # {{{ use_regex = false, use_tags = false) results = [] # Search in repo repo.each do |s| if !query.nil? and (s.name.downcase == query.downcase or (use_regex and s.name.downcase.match(query)) or (use_tags and s.tags.include?(query.capitalize))) # Check version? if version.nil? or s.version == version results.push(s) end end end results end # }}} def colorize(color, text, bold = false, mode = :fg) # {{{ c = true == bold ? 1 : 30 + color #< Bold mode m = :fg == mode ? 1 : 3 #< Color mode "\033[#{m};#{c}m#{text}\033[m" end # }}} def build_local(force = false) # {{{ @cache_local = [] # Load local cache if !force and File.exist?(@path_local) yaml = YAML::load(File.open(@path_local)) @cache_local = YAML::load(yaml) return end # Check installed sublets Dir[@path_specs + "/*"].each do |file| begin spec = Sur::Specification.load_spec(file) # Validate if spec.valid? spec.path = file @cache_local.push(spec) else spec.validate end rescue puts ">>> WARNING: Cannot parse specification `#{file}'" end end puts ">>> Updated local cache with #{@cache_local.size} entries" @cache_local.sort { |a, b| [ a.name, a.version ] <=> [ b.name, b.version ] } # Dump yaml file yaml = @cache_local.to_yaml File.open(@path_local, "w+") do |out| YAML::dump(yaml, out) end end # }}} def build_remote(force = false) # {{{ @cache_remote = [] uri = URI.parse(HOST) http = Net::HTTP.new(uri.host, uri.port) # Check age of cache if !force and File.exist?(@path_remote) and 86400 > (Time.now - File.new(@path_remote).ctime) yaml = YAML::load(File.open(@path_remote)) @cache_remote = YAML::load(yaml) return end # Fetch file http.request_get("/list") do |response| # Check result case response.code.to_i when 200 total = 0 data = "" # Downloading list response.read_body do |str| total += str.length data << str progress(">>> Fetching list", response.content_length, total) end puts # Reading list @cache_remote = YAML::load(YAML::load(data)) @cache_remote.sort do |a, b| [ a.name, a.version ] <=> [ b.name, b.version ] end # Dump yaml file yaml = @cache_remote.to_yaml File.open(@path_remote, "w+") do |out| YAML::dump(yaml, out) end puts ">>> Updated remote cache with #{@cache_remote.size} entries" else raise "Cannot download sublet list: Server error" end end end # }}} def compact_list(specs) # {{{ list = [] # Skip if specs list is empty if specs.any? prev = nil specs.sort { |a, b| [ a.name, a.version ] <=> [ b.name, b.version ] }.reverse! # Compress versions specs.each do |s| if !prev.nil? and prev.name == s.name if prev.version.is_a?(Array) prev.version << s.version else prev.version = [ prev.version, s.version ] end else list << prev unless prev.nil? prev = s end end list << prev unless prev.nil? and prev.name == s.name end list end # }}} def show_list(specs, use_color) # {{{ specs = compact_list(specs) i = 1 specs.each do |s| # Find if installed installed = "" @cache_local.each do |cs| if cs.name == s.name installed = "[%s installed]" % [ cs.version ] break end end # Convert version array to string version = s.version.is_a?(Array) ? s.version.join(", ") : s.version # Do we like colors? if use_color puts "%s %s (%s) %s" % [ colorize(2, i.to_s, false, :bg), colorize(1, s.name.downcase, true), colorize(2, version), colorize(2, installed, false, :bg) ] puts " %s" % [ s.description ] unless s.tags.empty? puts " %s" % [ s.tags.map { |t| colorize(5, "##{t}") }.join(" ") ] end else puts "(%d) %s (%s) %s" % [ i, s.name.downcase, version, installed ] puts " %s" % [ s.description ] unless s.tags.empty? puts " %s" % [ s.tags.map { |t| "##{t}" }.join(" ") ] end end i += 1 end end # }}} def show_notes(spec) # {{{ unless spec.notes.nil? or spec.notes.empty? puts puts spec.notes puts end end # }}} def show_config(spec, use_color) # {{{ unless spec.nil? puts # Add default config settings config = [ { :name => "interval", :type => "integer", :description => "Update interval in seconds", :def_value => "60" }, { :name => "style", :type => "string", :description => "Default sublet style (sub-style of sublets)", :def_value => "Sublet name" } ] # Merge configs unless spec.config.nil? skip = [] spec.config.each do |c| case c[:name] when "interval" then skip << "interval" when "style" then skip << "style" end end config.delete_if { |c| skip.include?(c[:name]) } config |= spec.config end # Header if use_color puts "%-24s %-19s %-39s %s" % [ colorize(1, "Name", true), colorize(1, "Type", true), colorize(1, "Default value", true), colorize(1, "Description", true) ] else puts "%-15s %-10s %-30s %s" % [ "Name", "Type", "Default value", "Description" ] end # Dump all settings config.each do |c| if use_color puts "%-25s %-20s %-40s %s" % [ colorize(2, c[:name][0..24]).ljust(25), colorize(5, c[:type][0..19]).ljust(20), colorize(3, c[:def_value]) || "", c[:description] ] else puts "%-14s %9s %-30s %s" % [ c[:name][0..15].ljust(15), c[:type][0..10].ljust(10), c[:def_value] || "", c[:description] ] end end puts end end # }}} def show_grabs(spec, use_color) # {{{ unless spec.nil? or spec.grabs.nil? or spec.grabs.empty? puts # Header if use_color puts "%-24s %s" % [ colorize(1, "Name", true), colorize(1, "Description", true) ] else puts "%-15s %s" % [ "Name", "Description" ] end # Dump all settings spec.grabs.each do |k, v| if use_color puts "%-25s %s" % [ colorize(2, k[0..24]).ljust(25), v ] else puts "%-14s %s" % [ k[0..15].ljust(15), v ] end end puts end end # }}} def show_info(spec, use_color) # {{{ unless spec.nil? authors = spec.authors.join(", ") tags = spec.tags.map { |t| "##{t}" }.join(" ") files = spec.files.join(", ") icons = spec.icons.map { |i| File.basename(i) }.join(", ") deps = spec.dependencies.map { |k, v| "#{k} (#{v})" }.join(", ") if use_color puts <<-EOF #{colorize(1, "Name:", true)} #{spec.name} #{colorize(1, "Version:", true)} #{colorize(2, spec.version)} #{colorize(1, "Authors:", true)} #{authors} #{colorize(1, "Contact:", true)} #{colorize(6, spec.contact)} #{colorize(1, "Tags:", true)} #{colorize(5, tags)} #{colorize(1, "Files:", true)} #{files} #{colorize(1, "Icons:", true)} #{icons} #{colorize(1, "Deps:", true)} #{spec.dependencies.map { |k, v| "#{k} (#{colorize(2, v)})" }.join(", ")} EOF else puts <<-EOF Name: #{spec.name} Version: #{spec.version} Authors: #{authors} Contact: #{spec.contact} Tags: #{tags} Files: #{files} Icons: #{icons} Deps: #{spec.dependencies.map { |k, v| "#{k} (#{v})" }.join(", ")} EOF end end end # }}} def install_sublet(sublet, specs = @path_specs, # {{{ icons = @path_icons, sublets = @path_sublets) spec = Sur::Specification.extract_spec(sublet) # Open sublet and install files File.open(sublet, "rb") do |input| Archive::Tar::Minitar::Input.open(input) do |tar| tar.each do |entry| case File.extname(entry.full_name) when ".spec" then puts ">>>>>> Installing specification `#{spec.to_s}.spec'" path = File.join(specs, spec.to_s + ".spec") when ".xbm", ".xpm" then puts ">>>>>> Installing icon `#{entry.full_name}'" path = File.join(icons, entry.full_name) else puts ">>>>>> Installing file `#{entry.full_name}'" path = File.join(sublets, entry.full_name) end # Install file File.open(path, "w+") do |output| output.write(entry.read) end end end puts ">>> Installed sublet #{spec.to_str}" show_notes(spec) end end # }}} def reload_sublets # {{{ begin require "subtle/subtlext" Subtlext::Subtle.reload rescue raise "Cannot reload sublets" end end # }}} end # }}} end # }}} end # }}} # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/data/sur/version.rb0000644000175000017500000000066011770332063017750 0ustar formorerformorer# -*- encoding: utf-8 -*- # # @package sur # # @file Sur version # @author Christoph Kappel # @version $Id: data/sur/version.rb,v 3182 2012/02/04 16:39:33 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # module Subtle # {{{ module Sur # {{{ # SUR version VERSION = "0.2" end # }}} end # }}} # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/data/sur/server.rb0000644000175000017500000003406411770332063017576 0ustar formorerformorer# -*- encoding: utf-8 -*- # # @package sur # # @file Server functions # @author Christoph Kappel # @version $Id: data/sur/server.rb,v 3182 2012/02/04 16:39:33 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # require "rubygems" require "fileutils" require "yaml" require "sinatra" require "haml" require "datamapper" require "digest/md5" require "uri" require "net/http" require "xmlrpc/marshal" require "subtle/sur/specification" module Subtle # {{{ module Sur # {{{ # Model classes for datamapper module Model # {{{ # Sublet class database model class Sublet # {{{ include DataMapper::Resource property(:id, Serial) property(:name, String, :unique_index => :name) property(:contact, String, :length => 255) property(:description, String, :length => 255) property(:version, String, :unique_index => :name) property(:date, DateTime) property(:path, String, :length => 255) property(:digest, String, :length => 255) property(:ip, String, :length => 255) property(:required, String, :required => false) property(:downloads, Integer, :default => 0) property(:annotations, Integer, :default => 0) property(:created_at, DateTime) has(n, :authors, :model => "Sur::Model::Assoc::Author") has(n, :tags, :model => "Sur::Model::Assoc::Tag") end # }}} # Tag class database model class Tag # {{{ include DataMapper::Resource property(:id, Serial) property(:name, String, :length => 255, :unique => true) property(:created_at, DateTime) has(n, :taggings, :model => "Sur::Model::Assoc::Tag") end # }}} # User class database model class User # {{{ include DataMapper::Resource property(:id, Serial) property(:name, String, :length => 255, :unique => true) property(:created_at, DateTime) end # }}} # Assoc classes for datamapper module Assoc # Tagging class database model class Author # {{{ include DataMapper::Resource property(:id, Serial) property(:user_id, Integer, :unique_index => :author) property(:sublet_id, Integer, :unique_index => :author) property(:created_at, DateTime) belongs_to(:user, :model => "Sur::Model::User") belongs_to(:sublet, :model => "Sur::Model::Sublet") end # }}} # Tag class database model class Tag # {{{ include DataMapper::Resource property(:id, Serial) property(:tag_id, Integer, :unique_index => :tag) property(:sublet_id, Integer, :unique_index => :tag) property(:created_at, DateTime) belongs_to(:tag, :model => "Sur::Model::Tag") belongs_to(:sublet, :model => "Sur::Model::Sublet") end # }}} # Tag class database model class Annotate # {{{ include DataMapper::Resource property(:id, Serial) property(:sublet_id, Integer) property(:user_id, Integer) property(:created_at, DateTime) belongs_to(:sublet, :model => "Sur::Model::Sublet") belongs_to(:user, :model => "Sur::Model::User") end # }}} end end # }}} # Server class for repository management class Server # {{{ # Database file DATABASE = Dir.pwd + "/repository.db" # Repository storage REPOSITORY = Dir.pwd + "/repository" # Cache file CACHE = Dir.pwd + "/cache.yaml" # Identi.ca username USERNAME = "subtle" # Identi.ca password PASSWORD = "" ## Sur::Server::initialize {{{ # Create a new Server object # # @param [Number] port Default port # @return [Object] New Sur::Server # # @since 0.0 # # @example # client = Sur::Server.new # => # def initialize(port = 4567) DataMapper.setup(:default, "sqlite3://" + DATABASE) #DataMapper::Model.raise_on_save_failure = true # Create database and store DataMapper.auto_migrate! unless File.exists?(DATABASE) Dir.mkdir(REPOSITORY) unless File.exists?(REPOSITORY) # Configure sinatra application set :port, port end # }}} ## Sur::Server::run {{{ # Run sinatra application # # @since 0.0 # # @example # Sur::Server.new.run # => nil def run helpers do # {{{ def build_cache # :nodoc: {{{ list = [] # Fetch sublets from database Sur::Model::Sublet.all(:order => [ :name, :version.desc ]).each do |s| # Collect authors authors = [] Sur::Model::Assoc::Author.all(:sublet_id => s.id).each do |a| authors.push(a.user.name) end # Collect tags tags = [] Sur::Model::Assoc::Tag.all(:sublet_id => s.id).each do |a| tags.push(a.tag.name) end # Create spec spec = Sur::Specification.new do |spec| spec.name = s.name spec.authors = authors spec.contact = s.contact spec.description = s.description spec.version = s.version spec.date = s.date spec.tags = tags spec.digest = s.digest spec.required_version = s.required end list.push(spec) end # Store cache yaml = list.to_yaml File.open(Sur::Server::CACHE, "w+") do |out| YAML::dump(yaml, out) end puts ">>> Regenerated sublets cache with #{list.size} sublets" end # }}} def get_all_sublets_in_interval(args) # :nodoc: {{{ sublets = [] # Find sublets in interval if 2 == args.size Sur::Model::Sublet.all( :created_at.gte => Time.at(args[0]), :created_at.lte => Time.at(args[1]) ).each do |s| sublets << "%s-%s" % [ s.name, s.version ] end end XMLRPC::Marshal.dump_response(sublets) end # }}} end # }}} # Templates template :layout do # {{{ < "text/css"} :sass body :color #000000 :background-color #ffffff :font-family Verdana,sans-serif :font-size 12px :margin 0px :padding 0px h1 :color #444444 :font-size 20px :margin 0 0 10px :padding 2px 10px 1px 0 h2 :color #444444 :font-size 16px :margin 0 0 10px :padding 2px 10px 1px 0 input :margin-top 1px :margin-bottom 1px :vertical-align middle input[type="text"] :border 1px solid #D7D7D7 input[type="text"]:focus :border 1px solid #888866 input[type="submit"] :background-color #F2F2F2 :border 1px outset #CCCCCC :color #222222 input[type="submit"]:hover :background-color #CCCCBB a#download:link, a#download:visited, a#download:active :color #000000 :text-decoration none :border-bottom 1px dotted #E4E4E4 a#download:hover :color #000000 :text-decoration none a:link, a:visited, a:active :color #59554e :text-decoration underline a:hover :text-decoration none .center :margin 0px auto .italic :font-style italic .gray :color #999999 #box :background-color #FCFCFC :border 1px solid #eee9de :color #505050 :line-height 1.5em :padding 6px :margin-bottom 10px :margin-right 10px :margin-top 10px :float left :min-width 280px :min-height 240px #form :width 900px :padding 10px 0px 10px 0px #frame :padding 10px #left :margin-right 10px :margin-top 10px :float left :width 630px #right :margin-right 10px :margin-top 10px :float right :width 230px :text-align right #clear :clear both %body #frame =yield EOF end # }}} template :index do # {{{ < "_parent", :href => "http://en.wikipedia.org/wiki/Domain_Specific_Language"} DSL that provides things like system information for the %a{:target => "_parent", :href => "http://subtle.subforge.org"} subtle panel. #form #left %form{:method => "get", :action => "/search"} Search: %input{:type => "text", :size => 40, :name => "query"} %input{:type => "submit", :name => "submit", :value => "Go"} #right %a{:target => "_parent", :href => "http://subforge.org/wiki/subtle/Specification"} Sublet specification | %a{:href => "http://sur.subforge.org/sublets"} All sublets #clear #box %h2 Latest sublets %ul -@newest.each do |s| %li %a#download{:href => "http://sur.subforge.org/get/%s" % [ s.digest ] } ="%s-%s" % [ s.name, s.version ] ="(%s)" % [ s.tags.map { |t| '#%s' % [ t.tag.name, t.tag.name ] }.join(", ") ] #box %h2 Most downloaded %ul -@most.each do |s| %li %a#download{:href => "http://sur.subforge.org/get/%s" % [ s.digest ] } ="%s-%s" % [ s.name, s.version ] ="(%d)" % [ s.downloads ] #box %h2 Never downloaded %ul -@never.each do |s| %li %a#download{:href => "http://sur.subforge.org/get/%s" % [ s.digest ] } ="%s-%s" % [ s.name, s.version ] ="(%s)" % [ s.tags.map { |t| '#%s' % [ t.tag.name, t.tag.name ] }.join(", ") ] %h2 Just broken %ul -@worst.each do |s| %li %a#download{:href => "http://sur.subforge.org/get/%s" % [ s.digest ] } ="%s-%s" % [ s.name, s.version ] ="(%d)" % [ s.annotations ] EOF end # }}} template :tag do # {{{ < "/get/%s" % [ s.digest ] } ="%s-%s" % [ s.name, s.version ] ="(%s)" % [ s.tags.map { |t| '#%s' % [ t.tag.name, t.tag.name ] }.join(", ") ] #clear .center %a{:href => "javascript:history.back()"} Back EOF end # }}} template :sublets do # {{{ < "/get/%s" % [ s.digest ] } ="%s-%s" % [ s.name, s.version ] ="(%s)" % [ s.tags.map { |t| '#%s' % [ t.tag.name, t.tag.name ] }.join(", ") ] %span.gray= "(%d downloads)" % [ s.downloads ] #clear .center %a{:href => "javascript:history.back()"} Back EOF end # }}} template :search do # {{{ < "/get/%s" % [ s.digest ] } ="%s-%s" % [ s.name, s.version ] ="(%s)" % [ s.tags.map { |t| '#%s' % [ t.tag.name, t.tag.name ] }.join(", ") ] %span.gray= "(%d downloads)" % [ s.downloads ] #clear .center %a{:href => "javascript:history.back()"} Back EOF end # }}} # Handlers get "/" do # {{{ @newest = Sur::Model::Sublet.all(:order => [ :created_at.desc ], :limit => 10) @most = Sur::Model::Sublet.all(:downloads.gte => 0, :order => [ :downloads.desc ], :limit => 10) @never = Sur::Model::Sublet.all(:downloads => 0, :order => [ :created_at.asc ], :limit => 4) @worst = Sur::Model::Sublet.all(:annotations.gt => 0, :order => [ :annotations.asc ], :limit => 4) haml(:index) end # }}} get "/tag/:tag" do # {{{ @tag = params[:tag].capitalize @list = Sur::Model::Sublet.all( Sur::Model::Sublet.tags.tag.name => params[:tag] ) haml(:tag) end # }}} get "/sublets" do # {{{ @list = Sur::Model::Sublet.all(:order => [ :name.asc ]) haml(:sublets) end # }}} get "/search" do # {{{ @query = params[:query] @list = Sur::Model::Sublet.all( :name.like => params[:query].gsub(/\*/, "%") ) haml(:search) end # }}} post "/annotate" do # {{{ if (s = Sur::Model::Sublet.first(:digest => params[:digest])) # Find or create user u = Sur::Model::User.first_or_create( { :name => params[:user] }, { :created_at => Time.now } ) # Create annotation Sur::Model::Assoc::Annotate.create( :sublet_id => s.id, :user_id => u.id, :created_at => Time.now ) # Increase annotation count s.annotations = s.annotations + 1 s.save puts ">>> Added annotation from `#{u.name}` for `#{s.name}'" else puts ">>> WARNING: Cannot find sublet with digest `#{params[:digest]}`" halt 404 end end # }}} Sinatra::Application.run! end # }}} end # }}} end # }}} end # }}} # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/data/sur/test.rb0000644000175000017500000003440611770332063017247 0ustar formorerformorer# -*- encoding: utf-8 -*- # # @package sur # # @file Sublet test functions # @author Christoph Kappel # @version $Id: data/sur/test.rb,v 3182 2012/02/04 16:39:33 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # require "subtle/subtlext" module Subtle # {{{ module Sur # {{{ module Test # {{{ # Sublet class class Sublet # {{{ # Name of the sublet attr_accessor :name # Update interval attr_accessor :interval # Sublet data attr_accessor :data # Watch path attr_accessor :path # Visibility attr_accessor :visible # Config hash attr_accessor :config # Event list EVENTS = { :run => 1, :unload => 1, :mouse_down => 4, :mouse_over => 1, :mouse_out => 1 } # Hook list HOOKS = [ :start, :exit, :tile, :reload, :client_create, :client_configure, :client_focus, :client_gravity, :client_kill, :tag_create, :tag_kill, :view_create, :view_configure, :view_jump, :view_kill ] ## Subtle::Sur::Test::Sublet::initialize {{{ # Initialize a Sublet # # @since 0.0 # # @example # Subtle::Sublet.new # => # def initialize @visible = true @interval = 60 @config = {} end # }}} ## Subtle::Sur::Test::Sublet::method_missing {{{ # Dispatcher for Sublet instance # # @param [Symbol] meth Method name # @param [Array] args Argument array # # @since 0.1 def method_missing(meth, *args) ret = nil # Check if symbol is a method or a var if self.respond_to?(meth) ret = self.send(meth, args) else sym = ("@" + meth.to_s).to_sym #< Construct symbol # Check getter or setter if meth.to_s.index("=") sym = sym.to_s.chop.to_sym self.instance_variable_set(sym, args.first) elsif self.instance_variable_defined?(sym) ret = self.instance_variable_get(sym) end end ret end # }}} ## Subtle::Sur::Test::Sublet::interval {{{ # Get the interval of a Sublet # # @return [Fixnum] Interval time # # @since 0.0 # # @example # Subtle::Sublet.new.interval # => 60 def interval @interval end # }}} ## Subtle::Sur::Test::Sublet::interval= {{{ # Set the interval of a Sublet # # @param [Fixnum] interval Interval time # # @raise [String] Sublet error # @since 0.0 # # @example # Subtle::Sublet.new.interval = 60 # => nil def interval=(interval) raise ArgumentError.new("Unknown value type") unless interval.is_a?(Fixnum) @interval = interval end # }}} ## Subtle::Sur::Test::Sublet::data {{{ # Get the data of a Sublet # # @return [String] Sublet data # # @since 0.0 # # @example # Subtle::Sublet.new.data # => "Subtle" def data @data end # }}} ## Subtle::Sur::Test::Sublet::data= {{{ # Set the data of a Sublet # # @param [String] data Sublet data # # @raise [String] Sublet error # @since 0.0 # # @example # Subtle::Sublet.new.data = "subtle" # => nil def data=(data) raise ArgumentError.new("Unknown value type") unless data.is_a?(String) @data = data end # }}} ## Subtle::Sur::Test::Sublet::background= {{{ # Set the background of a Sublet # # @param [String] value Sublet background # # @raise [String] Sublet error # @since 0.2 # # @example # Subtle::Sublet.new.background = "#ffffff" # => nil def background=(value) unless value.is_a?(String) or value.is_a?(Object) raise ArgumentError.new("Unknown value type") end end # }}} ## Subtle::Sur::Test::Sublet::geometry {{{ # Get geometry of a Sublet # # @return [String] Sublet geometry # # @raise [String] Sublet error # @since 0.2 # # @example # Subtle::Sublet.new.geometry # => # def geometry Subtlext::Geometry.new(10, 10, 20, 20) end # }}} ## Subtle::Sur::Test::Sublet::watch {{{ # Watch a file found on path # # @raise [String] Sublet error # @since 0.0 # # @example # Subtle::Sublet.new.watch("/tmp/watch") # => nil def watch(path) raise ArgumentError.new("Unknown value type") unless path.is_a?(String) raise "File not found" unless File.exist?(path) @path = path end # }}} ## Subtle::Sur::Test::Sublet::unwatch {{{ # Stop watching a file # # @since 0.0 # # @example # Subtle::Sublet::unwatch # => nil def unwatch @path = nil end # }}} ## Subtle::Sur::Test::Sublet::show {{{ # Show sublet on panel # # @since 0.2 # # @example # Subtle::Sublet.new.show # => nil def show @visible = true end # }}} ## Subtle::Sur::Test::Sublet::hide {{{ # Hide sublet from panel # # @since 0.2 # # @example # Subtle::Sublet.new.show # => nil def hide @visible = false end # }}} ## Subtle::Sur::Test::Sublet::hidden? {{{ # Whether sublet is hidden # # @since 0.2 # # @example # Subtle::Sublet.new.show # => nil def hidden? !@visible end # }}} ## Subtle::Sur::Test::Sublet::configure {{{ # Configure block for Sublet # # @param [Symbol] name Sublet name # # @yield [Object] New Sublet # # @raise [String] Sublet error # @since 0.1 # # @example # configure :sublet do |s| # s.interval = 60 # end def configure(name, &blk) raise LocalJumpError.new("No block given") unless block_given? raise ArgumentError.new("Unknown value type") unless name.is_a?(Symbol) @name = name # Add configure method self.class.send(:define_method, :__configure, blk) end # }}} ## Subtle::Sur::Test::Sublet::on {{{ # Event block for Sublet # # @param [Symbol] event Event name # # @yield [Proc] Event handler # # @raise [String] Sublet error # @since 0.1 # # @example # on :event do |s| # puts s.name # end def on(event, &blk) raise LocalJumpError.new("No block given") unless block_given? raise ArgumentError.new("Unknown value type") unless event.is_a?(Symbol) # Get proc information arity = blk.arity sing = self.class # Check events if Subtle::Sur::Test::Sublet::EVENTS.has_key?(event) need = Subtle::Sur::Test::Sublet::EVENTS[event] if -1 == arity or (1 <= arity and need >= arity) sing.send(:define_method, ("__%s" % [ event ]).to_sym, blk) else raise "Wrong number of arguments (%d for %d)" % [ arity, need ] end end # Check hooks if Subtle::Sur::Test::Sublet::HOOKS.include?(event) sing.send(:define_method, ("__%s" % [ event ]).to_sym, blk) end end # }}} ## Subtle::Sur::Test::Sublet::helper {{{ # Helper block for Sublet # # @yield [Proc] Helper handler # # @raise [String] Sublet error # @since 0.1 # # @example # helper do |s| # def test # puts "test" # end # end def helper(&blk) raise LocalJumpError.new("No block given") unless block_given? # Yield and eval block yield(self) self.instance_eval(&blk) end # }}} end # }}} # Test class for Dummy class Dummy # {{{ ## Subtle::Sur::Test::Dummy::method_missing {{{ # Dispatcher for Dummy instance # # @param [Symbol] meth Method name # @param [Array] args Argument array # # @since 0.1 def method_missing(meth, *args) ret = nil # Check if symbol is a method or a var if self.respond_to?(meth) ret = self.send(meth, args) else sym = ("@" + meth.to_s).to_sym #< Construct symbol # Check getter or setter if meth.to_s.index("=") sym = sym.to_s.chop.to_sym self.instance_variable_set(sym, args.first) else ret = self.instance_variable_get(sym) ret = self if ret.nil? end end ret end # }}} end # }}} ## Subtle::Sur::Test::run {{{ # Run test for every file in args # # @param [Array] config Config values # @param [Array] args Args array # # @example # Subtle::Sur::Test::Subtle.new.run(args) # => nil def self.run(config, args) args.each do |arg| # Load sublet if File.exist?(arg) begin sublet = Subtle::Sur::Test::Sublet.new # Eval sublet in anonymous module sublet.instance_eval(File.read(arg)) rescue => err puts ">>> WARNING: Cannot load sublet: %s" % [ err.message ] unless error.is_a?(RuntimeError) puts error.backtrace end next end # Check if sublet exists unless sublet.nil? methods = [] dummy = Subtle::Sur::Test::Dummy.new # Apply config values sublet.config = parse_config(config) # Configure and run sublet sublet.__configure(sublet) sublet.__run(sublet) if sublet.respond_to?(:__run) # Sanitize if !sublet.instance_variable_defined?("@interval") or 0 >= sublet.interval sublet.interval = 60 end # Collect events Subtle::Sur::Test::Sublet::EVENTS.each do |k, v| name = ("__%s" % [ k ]).to_sym if sublet.respond_to?(name) methods.push({ :name => k, :arity => sublet.method(name).arity, :singleton => false }) end end # Collect hooks Subtle::Sur::Test::Sublet::HOOKS.each do |k| name = ("__%s" % [ k ]).to_sym if sublet.respond_to?(name) methods.push({ :name => k, :arity => sublet.method(name).arity, :singleton => false }) end end # Collect singleton methods sublet.singleton_methods.each do |k| methods.push({ :name => k, :arity => sublet.method(k).arity, :singleton => true }) end begin puts "------------------" puts " %s" % [ sublet.name ] puts "------------------" # Show instance variables sublet.instance_variables.each do |v| puts " %s = %s" % [ v, sublet.instance_variable_get(v) ] end puts "------------------" # Show method list i = 1 methods.each do |m| puts " (%d) %s %s" % [ i, m[:name], (m[:singleton] ? "(helper)" : "") ] i += 1 end puts " (0) Quit" puts "------------------" puts ">>> Select one method:\n" printf ">>> " num = STDIN.readline.to_i begin if 0 < num and methods.size >= num meth = methods[num - 1] name = meth[:singleton] ? meth[:name] : ("__%s" % [ meth[:name] ]).to_sym # Check proc arity case meth[:arity] when 2 then sublet.send(name, sublet, dummy) when 4 then sublet.send(name, sublet, 5, 5, 1) when 0 then sublet.send(name) else sublet.send(name, sublet) end end rescue => error puts ">>> ERROR: #{error}" unless error.is_a?(RuntimeError) puts error.backtrace end end end while(0 != num) end end end end # }}} private def self.parse_config(config) # {{{ hash = {} config.each do |c| key, value = c.split("=") hash[key] = value end hash rescue => error puts ">>> ERROR: #{error}" {} end # }}} end # }}} end # }}} end # }}} # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/data/subtler/0000755000175000017500000000000011770527221016605 5ustar formorerformorersubtle-0.11.3224-xi/data/subtler/runner.rb0000644000175000017500000004221311770332063020443 0ustar formorerformorer#!/usr/bin/ruby # -*- encoding: utf-8 -*- # # @package subtler # # @file Subtle remote # @copyright (c) 2005-2012 Christoph Kappel # @version $Id: data/subtler/runner.rb,v 3182 2012/02/04 16:39:33 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # require 'getoptlong' require 'subtle/subtlext' module Subtle # {{{ module Subtler # {{{ class Runner # {{{ # Signals trap 'INT' do exit end ## initialize {{{ # Intialize class ## def initialize @mod = nil @group = nil @action = nil @proc = nil # Getopt options @opts = GetoptLong.new( # Groups [ '--Client', '-c', GetoptLong::NO_ARGUMENT ], [ '--Gravity', '-g', GetoptLong::NO_ARGUMENT ], [ '--Screen', '-e', GetoptLong::NO_ARGUMENT ], [ '--Sublet', '-s', GetoptLong::NO_ARGUMENT ], [ '--Tag', '-t', GetoptLong::NO_ARGUMENT ], [ '--Tray', '-y', GetoptLong::NO_ARGUMENT ], [ '--View', '-v', GetoptLong::NO_ARGUMENT ], # Actions [ '--add', '-a', GetoptLong::NO_ARGUMENT ], [ '--kill', '-k', GetoptLong::NO_ARGUMENT ], [ '--find', '-f', GetoptLong::NO_ARGUMENT ], [ '--focus', '-o', GetoptLong::NO_ARGUMENT ], [ '--full', '-F', GetoptLong::NO_ARGUMENT ], [ '--float', '-O', GetoptLong::NO_ARGUMENT ], [ '--stick', '-S', GetoptLong::NO_ARGUMENT ], [ '--urgent', '-N', GetoptLong::NO_ARGUMENT ], [ '--jump', '-j', GetoptLong::NO_ARGUMENT ], [ '--list', '-l', GetoptLong::NO_ARGUMENT ], [ '--tag', '-T', GetoptLong::NO_ARGUMENT ], [ '--untag', '-U', GetoptLong::NO_ARGUMENT ], [ '--tags', '-G', GetoptLong::NO_ARGUMENT ], [ '--retag', '-A', GetoptLong::NO_ARGUMENT ], [ '--clients', '-I', GetoptLong::NO_ARGUMENT ], [ '--views', '-W', GetoptLong::NO_ARGUMENT ], [ '--update', '-u', GetoptLong::NO_ARGUMENT ], [ '--data', '-D', GetoptLong::NO_ARGUMENT ], [ '--gravity', '-Y', GetoptLong::NO_ARGUMENT ], [ '--screen', '-n', GetoptLong::NO_ARGUMENT ], [ '--raise', '-E', GetoptLong::NO_ARGUMENT ], [ '--lower', '-L', GetoptLong::NO_ARGUMENT ], # Modifiers [ '--reload', '-r', GetoptLong::NO_ARGUMENT ], [ '--restart', '-R', GetoptLong::NO_ARGUMENT ], [ '--quit', '-q', GetoptLong::NO_ARGUMENT ], [ '--current', '-C', GetoptLong::NO_ARGUMENT ], [ '--select', '-X', GetoptLong::NO_ARGUMENT ], [ '--proc', '-p', GetoptLong::REQUIRED_ARGUMENT ], # Other [ '--display', '-d', GetoptLong::REQUIRED_ARGUMENT ], [ '--help', '-h', GetoptLong::NO_ARGUMENT ], [ '--version', '-V', GetoptLong::NO_ARGUMENT ] ) end # }}} ## run {{{ # Run subtler ## def run # Parse arguments @opts.each do |opt, arg| case opt # Groups when '--Client' then @group = Subtlext::Client when '--Gravity' then @group = Subtlext::Gravity when '--Screen' then @group = Subtlext::Screen when '--Sublet' then @group = Subtlext::Sublet when '--Tag' then @group = Subtlext::Tag when '--Tray' then @group = Subtlext::Tray when '--View' then @group = Subtlext::View # Actions when '--add' then @action = :new when '--kill' then @action = :kill when '--find' then @action = :find when '--focus' then @action = :focus when '--full' then @action = :toggle_full when '--float' then @action = :toggle_float when '--stick' then @action = :toggle_stick when '--urgent' then @action = :toggle_urgent when '--jump' then @action = :jump when '--list' then @action = :list when '--tag' then @action = :tag when '--untag' then @action = :untag when '--tags' then @action = :tags when '--retag' then @action = :retag when '--clients' then @action = :clients when '--views' then @action = :views when '--update' then @action = :update when '--data' then @action = :send_data when '--gravity' then @action = :gravity= when '--raise' then @action = :raise when '--lower' then @action = :lower # Modifiers when '--reload' then @mod = :reload when '--restart' then @mod = :restart when '--quit' then @mod = :quit when '--current' then @mod = :current when '--select' then @mod = :select # Other when '--proc' @proc = Proc.new { |param| eval(arg) } when '--display' Subtlext::Subtle.display = arg when '--help' usage(@group) exit when '--version' version exit else usage exit end end # Get arguments arg1 = ARGV.shift arg2 = ARGV.shift # Convert window ids arg1 = Integer(arg1) rescue arg1 arg2 = Integer(arg2) rescue arg2 # Pipes? arg1 = ARGF.read.chop if '-' == arg1 if '-' == arg2 # Read pipe until EOF begin while (arg2 = ARGF.readline) do handle_command(arg1, arg2.chop) end rescue EOFError # Just ignore this end else handle_command(arg1, arg2) end end # }}} private def handle_command(arg1, arg2) # {{{ # Modifiers case @mod when :reload then Subtlext::Subtle.reload when :restart then Subtlext::Subtle.restart when :quit then Subtlext::Subtle.quit when :current arg2 = arg1 arg1 = :current when :select arg2 = arg1 arg1 = Subtlext::Subtle.select_window end # Call method begin if !@group.nil? and !@action.nil? # Check singleton and instance methods if (@group.singleton_methods << :new).include?(@action) obj = @group args = [ arg1, arg2 ].compact arity = obj.method(@action).arity elsif @group.instance_methods.include?(@action) obj = @group.send(:find, arg1) args = [ arg2 ] arity = @group.instance_method(@action).arity end # Handle different arities case arity when 1 # Handle different return types if obj.is_a?(Array) obj.each do |o| p '%s:' % o if 1 < obj.size handle_result(o.send(@action, *args)) end else handle_result(obj.send(@action, *args)) end when -1 if [ Subtlext::Sublet, Subtlext::Tag, Subtlext::View, Subtlext::Gravity ].include?(@group) # Create new object ret = obj.send(@action, *args) ret.save handle_result(ret) end exit when nil usage(@group) exit else # Handle different return types if obj.is_a?(Array) obj.each do |o| p '%s:' % o if 1 < obj.size handle_result(o.send(@action)) end else handle_result(obj.send(@action)) end end elsif :reload != @mod and :restart != @mod and :quit != @mod usage(@group) exit end rescue ArgumentError usage(@group) exit end end # }}} def call_or_print(value) # {{{ unless @proc.nil? @proc.call(value) else printer(value) end end # }}} def handle_result(result) # {{{ case result when Array result.each do |r| call_or_print(r) end else call_or_print(result) end end # }}} def printer(value) # {{{ case value when Subtlext::Client # {{{ puts '%#10x %s %d %4d x %4d + %4d + %-4d %12.12s %s%s%s%s%s%s %s (%s)' % [ value.win, value.views.map { |v| v.name }.include?(Subtlext::View.current.name) ? '*' : '-', Subtlext::View.current.id, value.geometry.x, value.geometry.y, value.geometry.width, value.geometry.height, value.gravity.name, (value.is_full?) ? '+' : '-', (value.is_float?) ? '^' : '-', (value.is_stick?) ? '*' : '-', (value.is_resize?) ? '~' : '-', (value.is_zaphod?) ? '=' : '-', (value.is_fixed?) ? '!' : '-', value.instance, value.klass ] # }}} when Subtlext::Gravity # {{{ puts '%15s %3d x %-3d %3d + %-3d' % [ value.name, value.geometry.x, value.geometry.y, value.geometry.width, value.geometry.height, ] # }}} when Subtlext::Screen # {{{ puts '%d %4d x %-4d %4d + %-4d' % [ value.id, value.geometry.x, value.geometry.y, value.geometry.width, value.geometry.height, ] # }}} when Subtlext::Tag # {{{ puts value.name # }}} when Subtlext::Tray # {{{ puts '%#10x %s (%s)' % [ value.win, value.instance, value.klass ] # }}} when Subtlext::Sublet # {{{ puts '%s' % [ value.name ] # }}} when Subtlext::View # {{{ puts '%2d %s %s' % [ value.id, Subtlext::View.visible.include?(value) ? '*' : '-', value.name ] # }}} end end # }}} def usage(group) # {{{ puts 'Usage: subtler [GENERIC|MODIFIER] GROUP ACTION [ARG1] [ARG2]\n\n' if group.nil? puts <<-EOF Generic: -d, --display=DISPLAY Connect to DISPLAY (default: #{ENV['DISPLAY']}) -h, --help Show this help and exit -V, --version Show version info and exit Modifier: -r, --reload Reload config and sublets -R, --restart Restart subtle -q, --quit Quit subtle -C, --current Select current active window/view instead of passing it via argument -X, --select Select a window via pointer instead of passing it via argument Groups: -c, --Client Use client group -g, --Gravity Use gravity group -e, --Screen Use screen group -s, --Sublet Use sublet group -t, --Tag Use tag group -y, --Tray Use tray group -v, --View Use views group EOF end if group.nil? or Subtlext::Client == group puts <<-EOF Actions for clients (-c, --Client): -f, --find => -cf PATTERN Find client -o, --focus => -co PATTERN Set focus to client -F, --full => -cF PATTERN Toggle full -O, --float => -cO PATTERN Toggle float -S, --stick => -cS PATTERN Toggle stick -N, --urgent => -cN PATTERN Toggle urgent -l, --list List all clients -T, --tag => -c PATTERN -T NAME Add tag to client -U, --untag => -c PATTERN NAME Remove tag from client -G, --tags => -cG PATTERN Show client tags -Y, --gravity => -c PATTERN -Y PATTERN Set client gravity -E, --raise => -cE PATTERN Raise client window -L, --lower => -cL PATTERN Lower client window -k, --kill => -ck PATTERN Kill client EOF end if group.nil? or Subtlext::Gravity == group puts <<-EOF Actions for gravities (-g, --Gravity): -a, --add => -g NAME -a GEOMETRY Create new gravity -l, --list List all gravities -f, --find => -gf PATTERN Find a gravity -k, --kill => -gk PATTERN Kill gravity EOF end if group.nil? or Subtlext::Screen == group puts <<-EOF Actions for screens (-e, --Screen): -l, --list List all screens -f, --find => -ef ID Find a screen EOF end if group.nil? or Subtlext::Sublet == group puts <<-EOF Actions for sublets (-s, --Sublet): -l, --list List all sublets -f, --find => -sf PATTERN Find sublet -u, --update => -su PATTERN Updates value of sublet -D, --data => -s PATTERN -D DATA Send data to sublet -k, --kill => -sk PATTERN Kill sublet EOF end if group.nil? or Subtlext::Tag == group puts <<-EOF Actions for tags (-t, --Tag): -a, --add => -ta NAME Create new tag -f, --find => -tf PATTERN Find all clients/views by tag -l, --list List all tags -I, --clients => -tI PATTERN Show clients with tag -k, --kill => -tk PATTERN Kill tag EOF end if group.nil? or Subtlext::Tray == group puts <<-EOF Actions for tray (-y, --Tray): -f, --find => -yf PATTERN Find all tray icons -l, --list List all tray icons -k, --kill => -yk PATTERN Kill tray icon EOF end if group.nil? or Subtlext::View == group puts <<-EOF Actions for views (-v, --View): -a, --add => -va NAME Create new view -f, --find => -vf PATTERN Find a view -l, --list List all views -T, --tag => -v PATTERN -T NAME Add tag to view -U, --untag => -v PATTERN -U NAME Remove tag from view -G, --tags Show view tags -I, --clients Show clients on view -k, --kill => -vk PATTERN Kill view EOF end puts <<-EOF Formats: Input: DISPLAY: : ID: GEOMETRY: x++ NAME: DATA PATTERN: Matching works either via plaintext, regex (see regex(7)), id or window id if applicable. If a pattern matches more than once ALL matches are used. If the PATTERN is '-' subtler will read from stdin. Output: Client listing: () Gravity listing: Screen listing: Tag listing: Tray listing: () View listing: Fields: Numeric (hex) id of window (e.g. 0xa00009) - = not visible, * = visible Numeric id of view (e.g. 5) x x y + width + height - = not set, + = fullscreen, ^ = float, * = stick, ~ = resize, = = zaphod, ! = fixed Window instance/resource name Window class name Numeric id of gravity (e.g. 2) Numeric id of a screen (e.g. 1) Name of a tag (e.g. terms) Examples: subtler -c -l List all clients subtler -t -a subtle Add new tag 'subtle' subtler -v subtle -T rocks Tag view 'subtle' with tag 'rocks' subtler -c xterm -G Show tags of client 'xterm' subtler -c -X -f Select client and show info subtler -c -C -Y 5 Set gravity 5 to current active client subtler -t -f term Show every client/view tagged with 'term' Please report bugs at http://subforge.org/projects/subtle/issues EOF end # }}} def version # {{{ puts <<-EOF subtler #{Subtlext::VERSION} - Copyright (c) 2005-2012 Christoph Kappel Released under the GNU General Public License Using Ruby #{RUBY_VERSION} EOF end # }}} end # }}} end # }}} end # }}} # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/data/bin/0000755000175000017500000000000011770527221015675 5ustar formorerformorersubtle-0.11.3224-xi/data/bin/surserver0000644000175000017500000000160511770332063017660 0ustar formorerformorer#!/usr/bin/ruby # -*- encoding: utf-8 -*- # # @package sur - surserver # # @file SUR - Subtle User Repository # @author Christoph Kappel # @version $Id: sur.rb,v 7 2009/09/07 21:25:11 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # begin require "subtle/sur/version" require "subtle/sur/server" Subtle::Sur::Server.new.run rescue LoadError => error missing = error.to_s.split(" ").last # Check missing files if "archive/tar/minitar" == missing missing = "archive-tar-minitar" end puts <>> ERROR: Cannot find the gem #{missing} >>> Please install it with following command: >>> gem install #{missing} EOF rescue => error puts ">>> ERROR: #{error}" unless error.is_a?(RuntimeError) puts error.backtrace end end # vim:ts=2:bs=2:sw=2:et:fdm=marker:ft=ruby subtle-0.11.3224-xi/data/bin/sur0000644000175000017500000000220711770332063016430 0ustar formorerformorer#!/usr/bin/ruby # -*- encoding: utf-8 -*- # # @package sur # # @file SUR - Subtle User Repository # @author Christoph Kappel # @version $Id: sur.rb,v 7 2009/09/07 21:25:11 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # begin require "subtle/sur/version" require "subtle/sur/runner" # Clone to change args = ARGV.clone Subtle::Sur::Runner.new.run(args) rescue LoadError => error missing = error.to_s.split(" ").last # Check missing files if "archive/tar/minitar" == missing missing = "archive-tar-minitar" elsif missing.match("subtle") puts <>> ERROR: Cannot find runtime files of sur >>> Please make sure that you installed subtle into the >>> current ruby environment (#{RUBY_VERSION}) EOF exit end puts <>> ERROR: Cannot find the gem `#{missing}' >>> Please install it with following command: >>> gem install #{missing} EOF rescue => error puts ">>> ERROR: #{error}" unless error.is_a?(RuntimeError) puts error.backtrace end end # vim:ts=2:bs=2:sw=2:et:fdm=marker:ft=ruby subtle-0.11.3224-xi/data/bin/subtler0000644000175000017500000000175011770332063017301 0ustar formorerformorer#!/usr/bin/ruby # -*- encoding: utf-8 -*- # # @package subtle # # @file Subtle remote # @author Christoph Kappel # @version $Id: sur.rb,v 7 2009/09/07 21:25:11 unexist $ # # This program can be distributed under the terms of the GNU GPL. # See the file COPYING. # begin require 'subtle/subtler/runner' Subtle::Subtler::Runner.new.run rescue LoadError => error missing = error.to_s.split(' ').last # Check missing files if missing.match('subtle') puts <<-EOF >>> ERROR: Cannot find runtime files of subtler >>> Please make sure that you installed subtle into the >>> current ruby environment (#{RUBY_VERSION}) EOF exit end puts <<-EOF >>> ERROR: Cannot find the gem #{missing} >>> Please install it with following command: >>> gem install #{missing} EOF rescue => error puts ">>> ERROR: #{error}" unless error.is_a?(RuntimeError) puts error.backtrace end end # vim:ts=2:bs=2:sw=2:et:fdm=marker:ft=ruby subtle-0.11.3224-xi/data/subtle.desktop0000644000175000017500000000015211770332063020012 0ustar formorerformorer[Desktop Entry] Encoding=UTF-8 Name=subtle Comment=subtle tiling window manager Exec=subtle Type=XSession subtle-0.11.3224-xi/data/subtle.rb0000644000175000017500000005635111770332063016760 0ustar formorerformorer# -*- encoding: utf-8 -*- # # Author:: Christoph Kappel # Version:: $Id: data/subtle.rb,v 3182 2012/02/04 16:39:33 unexist $ # License:: GNU GPLv2 # # = Subtle default configuration # # This file will be installed as default and can also be used as a starter for # an own custom configuration file. The system wide config usually resides in # +/etc/xdg/subtle+ and the user config in +HOME/.config/subtle+, both locations # are dependent on the locations specified by +XDG_CONFIG_DIRS+ and # +XDG_CONFIG_HOME+. # # # == Options # # Following options change behaviour and sizes of the window manager: # # Window move/resize steps in pixel per keypress set :increase_step, 5 # Window screen border snapping set :border_snap, 10 # Default starting gravity for windows. Comment out to use gravity of # currently active client set :default_gravity, :center # Make dialog windows urgent and draw focus set :urgent_dialogs, false # Honor resize size hints globally set :honor_size_hints, false # Enable gravity tiling for all gravities set :gravity_tiling, false # Enable click-to-focus focus model set :click_to_focus, false # Skip pointer movement on e.g. gravity change set :skip_pointer_warp, false # Skip pointer movement to urgent windows set :skip_urgent_warp, false # Set the WM_NAME of subtle (Java quirk) # set :wmname, "LG3D" # # == Screen # # Generally subtle comes with two panels per screen, one on the top and one at # the bottom. Each panel can be configured with different panel items and # sublets screen wise. The default config uses top panel on the first screen # only, it's up to the user to enable the bottom panel or disable either one # or both. # === Properties # # [*stipple*] This property adds a stipple pattern to both screen panels. # # Example: stipple "~/stipple.xbm" # stipple Subtlext::Icon.new("~/stipple.xbm") # # [*top*] This property adds a top panel to the screen. # # Example: top [ :views, :title ] # # [*bottom*] This property adds a bottom panel to the screen. # # Example: bottom [ :views, :title ] # # Following items are available for the panels: # # [*:views*] List of views with buttons # [*:title*] Title of the current active window # [*:tray*] Systray icons (Can be used only once) # [*:keychain*] Display current chain (Can be used only once) # [*:sublets*] Catch-all for installed sublets # [*:sublet*] Name of a sublet for direct placement # [*:spacer*] Variable spacer (free width / count of spacers) # [*:center*] Enclose items with :center to center them on the panel # [*:separator*] Insert separator # # Empty panels are hidden. # # === Links # # http://subforge.org/projects/subtle/wiki/Multihead # http://subforge.org/projects/subtle/wiki/Panel # screen 1 do top [ :views, :title, :spacer, :keychain, :spacer, :tray, :sublets ] bottom [ ] end # Example for a second screen: #screen 2 do # top [ :views, :title, :spacer ] # bottom [ ] #end # # == Styles # # Styles define various properties of styleable items in a CSS-like syntax. # # If no background color is given no color will be set. This will ensure a # custom background pixmap won't be overwritten. # # Following properties are available for most the styles: # # [*foreground*] Foreground text color # [*background*] Background color # [*margin*] Outer spacing # [*border*] Border color and size # [*padding*] Inner spacing # [*font*] Font string (xftontsel or xft) # # === Link # # http://subforge.org/projects/subtle/wiki/Styles # Style for all style elements style :all do background "#202020" icon "#757575" border "#303030", 0 padding 0, 3 font "-*-*-*-*-*-*-14-*-*-*-*-*-*-*" #font "xft:sans-8" end # Style for the all views style :views do foreground "#757575" # Style for the active views style :focus do foreground "#fecf35" end # Style for urgent window titles and views style :urgent do foreground "#ff9800" end # Style for occupied views (views with clients) style :occupied do foreground "#b8b8b8" end end # Style for sublets style :sublets do foreground "#757575" end # Style for separator style :separator do foreground "#757575" separator "|" end # Style for focus window title style :title do foreground "#fecf35" end # Style for active/inactive windows style :clients do active "#303030", 2 inactive "#202020", 2 margin 0 width 50 end # Style for subtle style :subtle do margin 0, 0, 0, 0 panel "#202020" background "#3d3d3d" stipple "#757575" end # # == Gravities # # Gravities are predefined sizes a window can be set to. There are several ways # to set a certain gravity, most convenient is to define a gravity via a tag or # change them during runtime via grab. Subtler and subtlext can also modify # gravities. # # A gravity consists of four values which are a percentage value of the screen # size. The first two values are x and y starting at the center of the screen # and he last two values are the width and height. # # === Example # # Following defines a gravity for a window with 100% width and height: # # gravity :example, [ 0, 0, 100, 100 ] # # === Link # # http://subforge.org/projects/subtle/wiki/Gravity # # Top left gravity :top_left, [ 0, 0, 50, 50 ] gravity :top_left66, [ 0, 0, 50, 66 ] gravity :top_left33, [ 0, 0, 50, 34 ] # Top gravity :top, [ 0, 0, 100, 50 ] gravity :top66, [ 0, 0, 100, 66 ] gravity :top33, [ 0, 0, 100, 34 ] # Top right gravity :top_right, [ 50, 0, 50, 50 ] gravity :top_right66, [ 50, 0, 50, 66 ] gravity :top_right33, [ 50, 0, 50, 33 ] # Left gravity :left, [ 0, 0, 50, 100 ] gravity :left66, [ 0, 0, 66, 100 ] gravity :left33, [ 0, 0, 33, 100 ] # Center gravity :center, [ 0, 0, 100, 100 ] gravity :center66, [ 17, 17, 66, 66 ] gravity :center33, [ 33, 33, 33, 33 ] # Right gravity :right, [ 50, 0, 50, 100 ] gravity :right66, [ 34, 0, 66, 100 ] gravity :right33, [ 67, 0, 33, 100 ] # Bottom left gravity :bottom_left, [ 0, 50, 50, 50 ] gravity :bottom_left66, [ 0, 34, 50, 66 ] gravity :bottom_left33, [ 0, 67, 50, 33 ] # Bottom gravity :bottom, [ 0, 50, 100, 50 ] gravity :bottom66, [ 0, 34, 100, 66 ] gravity :bottom33, [ 0, 67, 100, 33 ] # Bottom right gravity :bottom_right, [ 50, 50, 50, 50 ] gravity :bottom_right66, [ 50, 34, 50, 66 ] gravity :bottom_right33, [ 50, 67, 50, 33 ] # Gimp gravity :gimp_image, [ 10, 0, 80, 100 ] gravity :gimp_toolbox, [ 0, 0, 10, 100 ] gravity :gimp_dock, [ 90, 0, 10, 100 ] # # == Grabs # # Grabs are keyboard and mouse actions within subtle, every grab can be # assigned either to a key and/or to a mouse button combination. A grab # consists of a chain and an action. # # === Finding keys # # The best resource for getting the correct key names is # */usr/include/X11/keysymdef.h*, but to make life easier here are some hints # about it: # # * Numbers and letters keep their names, so *a* is *a* and *0* is *0* # * Keypad keys need *KP_* as prefix, so *KP_1* is *1* on the keypad # * Strip the *XK_* from the key names if looked up in # /usr/include/X11/keysymdef.h # * Keys usually have meaningful english names # * Modifier keys have special meaning (Alt (A), Control (C), Meta (M), # Shift (S), Super (W)) # # === Chaining # # Chains are a combination of keys and modifiers to one or a list of keys # and can be used in various ways to trigger an action. In subtle, there are # two ways to define chains for grabs: # # 1. *Default*: Add modifiers to a key and use it for a grab # # *Example*: grab "W-Return", "urxvt" # # 2. *Chain*: Define a list of grabs that need to be pressed in order # # *Example*: grab "C-y Return", "urxvt" # # ==== Mouse buttons # # [*B1*] = Button1 (Left mouse button) # [*B2*] = Button2 (Middle mouse button) # [*B3*] = Button3 (Right mouse button) # [*B4*] = Button4 (Mouse wheel up) # [*B5*] = Button5 (Mouse wheel down) # [*...*] # [*B20*] = Button20 (Are you sure that this is a mouse and not a keyboard?) # # ==== Modifiers # # [*A*] = Alt key (Mod1) # [*C*] = Control key # [*M*] = Meta key (Mod3) # [*S*] = Shift key # [*W*] = Super/Windows key (Mod4) # [*G*] = Alt Gr (Mod5) # # === Action # # An action is something that happens when a grab is activated, this can be one # of the following: # # [*symbol*] Run a subtle action # [*string*] Start a certain program # [*array*] Cycle through gravities # [*lambda*] Run a Ruby proc # # === Example # # This will create a grab that starts a urxvt when Alt+Enter are pressed: # # grab "A-Return", "urxvt" # grab "C-a c", "urxvt" # # === Link # # http://subforge.org/projects/subtle/wiki/Grabs # # Jump to view1, view2, ... grab "W-S-1", :ViewJump1 grab "W-S-2", :ViewJump2 grab "W-S-3", :ViewJump3 grab "W-S-4", :ViewJump4 # Switch current view grab "W-1", :ViewSwitch1 grab "W-2", :ViewSwitch2 grab "W-3", :ViewSwitch3 grab "W-4", :ViewSwitch4 # Select next and prev view */ grab "KP_Add", :ViewNext grab "KP_Subtract", :ViewPrev # Move mouse to screen1, screen2, ... grab "W-A-1", :ScreenJump1 grab "W-A-2", :ScreenJump2 grab "W-A-3", :ScreenJump3 grab "W-A-4", :ScreenJump4 # Force reload of config and sublets grab "W-C-r", :SubtleReload # Force restart of subtle grab "W-C-S-r", :SubtleRestart # Quit subtle grab "W-C-q", :SubtleQuit # Move current window grab "W-B1", :WindowMove # Resize current window grab "W-B3", :WindowResize # Toggle floating mode of window grab "W-f", :WindowFloat # Toggle fullscreen mode of window grab "W-space", :WindowFull # Toggle sticky mode of window (will be visible on all views) grab "W-s", :WindowStick # Toggle zaphod mode of window (will span across all screens) grab "W-equal", :WindowZaphod # Raise window grab "W-r", :WindowRaise # Lower window grab "W-l", :WindowLower # Select next windows grab "W-Left", :WindowLeft grab "W-Down", :WindowDown grab "W-Up", :WindowUp grab "W-Right", :WindowRight # Kill current window grab "W-S-k", :WindowKill # Cycle between given gravities grab "W-KP_7", [ :top_left, :top_left66, :top_left33 ] grab "W-KP_8", [ :top, :top66, :top33 ] grab "W-KP_9", [ :top_right, :top_right66, :top_right33 ] grab "W-KP_4", [ :left, :left66, :left33 ] grab "W-KP_5", [ :center, :center66, :center33 ] grab "W-KP_6", [ :right, :right66, :right33 ] grab "W-KP_1", [ :bottom_left, :bottom_left66, :bottom_left33 ] grab "W-KP_2", [ :bottom, :bottom66, :bottom33 ] grab "W-KP_3", [ :bottom_right, :bottom_right66, :bottom_right33 ] # In case no numpad is available e.g. on notebooks #grab "W-q", [ :top_left, :top_left66, :top_left33 ] #grab "W-w", [ :top, :top66, :top33 ] #grab "W-e", [ :top_right, :top_right66, :top_right33 ] #grab "W-a", [ :left, :left66, :left33 ] #grab "W-s", [ :center, :center66, :center33 ] #grab "W-d", [ :right, :right66, :right33 ] # # QUERTZ #grab "W-y", [ :bottom_left, :bottom_left66, :bottom_left33 ] # # QWERTY #grab "W-z", [ :bottom_left, :bottom_left66, :bottom_left33 ] # #grab "W-x", [ :bottom, :bottom66, :bottom33 ] #grab "W-c", [ :bottom_right, :bottom_right66, :bottom_right33 ] # Exec programs grab "W-Return", "urxvt" # Run Ruby lambdas grab "S-F2" do |c| puts c.name end grab "S-F3" do puts Subtlext::VERSION end # # == Tags # # Tags are generally used in subtle for placement of windows. This placement is # strict, that means that - aside from other tiling window managers - windows # must have a matching tag to be on a certain view. This also includes that # windows that are started on a certain view will not automatically be placed # there. # # There are to ways to define a tag: # # === Simple # # The simple way just needs a name and a regular expression to just handle the # placement: # # Example: # # tag "terms", "terms" # # === Extended # # Additionally tags can do a lot more then just control the placement - they # also have properties than can define and control some aspects of a window # like the default gravity or the default screen per view. # # Example: # # tag "terms" do # match "xterm|[u]?rxvt" # gravity :center # end # # === Default # # Whenever a window has no tag it will get the default tag and be placed on the # default view. The default view can either be set by the user with adding the # default tag to a view by choice or otherwise the first defined view will be # chosen automatically. # # === Properties # # [*borderless*] This property enables the borderless mode for tagged clients. # # Example: borderless true # Links: http://subforge.org/projects/subtle/wiki/Tagging#Borderless # http://subforge.org/projects/subtle/wiki/Clients#Borderless # # [*fixed*] This property enables the fixed mode for tagged clients. # # Example: fixed true # Links: http://subforge.org/projects/subtle/wiki/Tagging#Fixed # http://subforge.org/projects/subtle/wiki/Clients#Fixed # # [*float*] This property enables the float mode for tagged clients. # # Example: float true # Links: http://subforge.org/projects/subtle/wiki/Tagging#Float # http://subforge.org/projects/subtle/wiki/Clients#Float # # [*full*] This property enables the fullscreen mode for tagged clients. # # Example: full true # Links: http://subforge.org/projects/subtle/wiki/Tagging#Fullscreen # http://subforge.org/projects/subtle/wiki/Clients#Fullscreen # # [*geometry*] This property sets a certain geometry as well as floating mode # to the tagged client, but only on views that have this tag too. # It expects an array with x, y, width and height values whereas # width and height must be >0. # # Example: geometry [100, 100, 50, 50] # Link: http://subforge.org/projects/subtle/wiki/Tagging#Geometry # # [*gravity*] This property sets a certain to gravity to the tagged client, # but only on views that have this tag too. # # Example: gravity :center # Link: http://subforge.org/projects/subtle/wiki/Tagging#Gravity # # [*match*] This property adds matching patterns to a tag, a tag can have # more than one. Matching works either via plaintext, regex # (see man regex(7)) or window id. Per default tags will only # match the WM_NAME and the WM_CLASS portion of a client, this # can be changed with following possible values: # # [*:name*] Match the WM_NAME # [*:instance*] Match the first (instance) part from WM_CLASS # [*:class*] Match the second (class) part from WM_CLASS # [*:role*] Match the window role # [*:type*] Match the window type # # Examples: match instance: "urxvt" # match [:role, :class] => "test" # match "[xa]+term" # Link: http://subforge.org/projects/subtle/wiki/Tagging#Match # # [*position*] Similar to the geometry property, this property just sets the # x/y coordinates of the tagged client, but only on views that # have this tag, too. It expects an array with x and y values. # # Example: position [ 10, 10 ] # Link: http://subforge.org/projects/subtle/wiki/Tagging#Position # # [*resize*] This property enables the float mode for tagged clients. When set, # subtle honors size hints, that define various size constraints like # sizes for columns and rows of a terminal. # # Example: resize true # Links: http://subforge.org/projects/subtle/wiki/Tagging#Resize # http://subforge.org/projects/subtle/wiki/Clients#Resize # # [*stick*] This property enables the stick mode for tagged clients. When set, # clients are visible on all views, even when they don't have matching # tags. On multihead, sticky clients keep the screen they are assigned # on. # # Supported values are either true or a number to specify a screen. # # Example: stick true # stick 1 # Links: http://subforge.org/projects/subtle/wiki/Tagging#Stick # http://subforge.org/projects/subtle/wiki/Clients#Stick # # [*type*] This property sets the tagged client to be treated as a specific # window type though as the window sets the type itself. Following # types are possible: # # [*:desktop*] Treat as desktop window (_NET_WM_WINDOW_TYPE_DESKTOP) # Link: http://subforge.org/projects/subtle/wiki/Clients#Desktop # [*:dock*] Treat as dock window (_NET_WM_WINDOW_TYPE_DOCK) # Link: http://subforge.org/projects/subtle/wiki/Clients#Dock # [*:toolbar*] Treat as toolbar windows (_NET_WM_WINDOW_TYPE_TOOLBAR) # Link: http://subforge.org/projects/subtle/wiki/Clients#Toolbar # [*:splash*] Treat as splash window (_NET_WM_WINDOW_TYPE_SPLASH) # Link: http://subforge.org/projects/subtle/wiki/Clients#Splash # [*:dialog*] Treat as dialog window (_NET_WM_WINDOW_TYPE_DIALOG) # Link: http://subforge.org/projects/subtle/wiki/Clients#Dialog # # Example: type :desktop # Link: http://subforge.org/projects/subtle/wiki/Tagging#Type # # [*urgent*] This property enables the urgent mode for tagged clients. When set, # subtle automatically sets this client to urgent. # # Example: urgent true # Links: http://subforge.org/projects/subtle/wiki/Tagging#Stick # http://subforge.org/projects/subtle/wiki/Clients#Urgent # # [*zaphod*] This property enables the zaphod mode for tagged clients. When set, # the client spans across all connected screens. # # Example: zaphod true # Links: http://subforge.org/projects/subtle/wiki/Tagging#Zaphod # http://subforge.org/projects/subtle/wiki/Clients#Zaphod # # # === Link # # http://subforge.org/projects/subtle/wiki/Tagging # # Simple tags tag "terms", "xterm|[u]?rxvt" tag "browser", "uzbl|opera|firefox|navigator" # Placement tag "editor" do match "[g]?vim" resize true end tag "fixed" do geometry [ 10, 10, 100, 100 ] stick true end tag "resize" do match "sakura|gvim" resize true end tag "gravity" do gravity :center end # Modes tag "stick" do match "mplayer" float true stick true end tag "float" do match "display" float true end # Gimp tag "gimp_image" do match :role => "gimp-image-window" gravity :gimp_image end tag "gimp_toolbox" do match :role => "gimp-toolbox$" gravity :gimp_toolbox end tag "gimp_dock" do match :role => "gimp-dock" gravity :gimp_dock end tag "gimp_scum" do match role: "gimp-.*|screenshot" end # # == Views # # Views are the virtual desktops in subtle, they show all windows that share a # tag with them. Windows that have no tag will be visible on the default view # which is the view with the default tag or the first defined view when this # tag isn't set. # # Like tags views can be defined in two ways: # # === Simple # # The simple way is exactly the same as for tags: # # Example: # # view "terms", "terms" # # === Extended # # The extended way for views is also similar to the tags, but with fewer # properties. # # Example: # # view "terms" do # match "terms" # icon "/usr/share/icons/icon.xbm" # end # # === Properties # # [*match*] This property adds a matching pattern to a view. Matching # works either via plaintext or regex (see man regex(7)) and # applies to names of tags. # # Example: match "terms" # # [*dynamic*] This property hides unoccupied views, views that display no # windows. # # Example: dynamic true # # [*icon*] This property adds an icon in front of the view name. The # icon can either be path to an icon or an instance of # Subtlext::Icon. # # Example: icon "/usr/share/icons/icon.xbm" # icon Subtlext::Icon.new("/usr/share/icons/icon.xbm") # # [*icon_only*] This property hides the view name from the view buttons, just # the icon will be visible. # # Example: icon_only true # # # === Link # # http://subforge.org/projects/subtle/wiki/Tagging # view "terms", "terms|default" view "www", "browser" view "gimp", "gimp_.*" view "dev", "editor" # # == Sublets # # Sublets are Ruby scripts that provide data for the panel and can be managed # with the sur script that comes with subtle. # # === Example # # sur install clock # sur uninstall clock # sur list # # === Configuration # # All sublets have a set of configuration values that can be changed directly # from the config of subtle. # # There are three default properties, that can be be changed for every sublet: # # [*interval*] Update interval of the sublet # [*foreground*] Default foreground color # [*background*] Default background color # # sur can also give a brief overview about properties: # # === Example # # sur config clock # # The syntax of the sublet configuration is similar to other configuration # options in subtle: # # === Example # # sublet :clock do # interval 30 # foreground "#eeeeee" # background "#000000" # format_string "%H:%M:%S" # end # # === Link # # http://subforge.org/projects/subtle/wiki/Sublets # # # == Hooks # # And finally hooks are a way to bind Ruby scripts to a certain event. # # Following hooks exist so far: # # [*:client_create*] Called whenever a window is created # [*:client_configure*] Called whenever a window is configured # [*:client_focus*] Called whenever a window gets focus # [*:client_kill*] Called whenever a window is killed # # [*:tag_create*] Called whenever a tag is created # [*:tag_kill*] Called whenever a tag is killed # # [*:view_create*] Called whenever a view is created # [*:view_configure*] Called whenever a view is configured # [*:view_jump*] Called whenever the view is switched # [*:view_kill*] Called whenever a view is killed # # [*:tile*] Called on whenever tiling would be needed # [*:reload*] Called on reload # [*:start*] Called on start # [*:exit*] Called on exit # # === Example # # This hook will print the name of the window that gets the focus: # # on :client_focus do |c| # puts c.name # end # # === Link # # http://subforge.org/projects/subtle/wiki/Hooks # # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/NEWS0000644000175000017500000000012411770332063014706 0ustar formorerformorerNews ---- The latest news can be found here: http://subtle.de/projects/subtle/news subtle-0.11.3224-xi/.hgtags0000644000175000017500000000065111770332063015472 0ustar formorerformorerdcb21643ed886de945135af08729b4baed5a1132 0.9.2773 0c57ccd923e5aa814f8fc10aa203f34822e2d63b 0.11 dcb21643ed886de945135af08729b4baed5a1132 0.9.2773 0000000000000000000000000000000000000000 0.9.2773 dcb21643ed886de945135af08729b4baed5a1132 0.9 0c57ccd923e5aa814f8fc10aa203f34822e2d63b 0.11 0000000000000000000000000000000000000000 0.11 0000000000000000000000000000000000000000 0.11 134e82e33716f81f02bc8323764f77a1bf0e0245 0.11 subtle-0.11.3224-xi/.hg_archival.txt0000644000175000017500000000022311770332063017275 0ustar formorerformorerrepo: 40917ee3b688415e6d60213df618043669b32ed7 node: 627e105ca7d93f346e994b52fe58c26293c4f9f5 branch: default latesttag: 0.11 latesttagdistance: 1 subtle-0.11.3224-xi/ChangeLog0000644000175000017500000000000011770332063015752 0ustar formorerformorersubtle-0.11.3224-xi/INSTALL0000644000175000017500000000424711770332063015252 0ustar formorerformorerInstall ------- Dependencies ============ subtle has the following dependencies: 1. Ruby[1] (>=1.9) 2. Rake[2] (>=0.8) 3. X11[3] (>=6.8.2) And folowing optional gems for sur: 1. curb[4] (>=0.5.1.0) (sur only) 2. archive-tar-minitar[5] (>=0.5.2) 3. datamapper[6] (>=0.9.11) (surserver only) 4. sinatra[7] (>=0.9.4) (surserver only) Versions ======== Stable releases can be found here[8]. If you like you can clone the Mercurial[9] repository and install the developer version as followed: 1. hg clone http://hg.subforge.org/subtle 2. cd subtle 3. rake [options] 4. rake install The installer will copy the files to following XDG[10] default path: (see: rake help) * Binaries -> /usr/bin * Config -> /etc/xdg/subtle * Extension -> (Ruby Dir)/subtle * Scripts -> /usr/share/subtle The paths can be easily changed (see: rake help) and are printed after configuring is done. subtle[11] will create cache files to keep track of the settings, whenever you need to change a path later you need to delete the old caches with 'rake clobber'. Config ====== On start subtle[11] will check if a config file is available in the typical XDG paths and use it. A local config is preferred over a system wide. (see: $XDG_CONFIG_HOME) Then it will have a look for sublets[12] in the XDG paths too and load them accordingly. (see $XDG_DATA_HOME) * To create a user config just create '$XDG_CONFIG_HOME/subtle' and copy the latest config from the 'data' folder into it. * To enable sublets[12] create '$XDG_DATA_HOME/subtle/sublets' and create symlinks to the sublets[12]. (Load order is alphabetically) Always compare your existing config with the one of the repository/tarball to get the latest options and check the latest news. [1] http://www.ruby-lang.org [2] http://rake.rubyforge.org [3] http://xorg.freedesktop.org [4] http://curb.rubyforge.org [5] http://rubyforge.org/projects/ruwiki [6] http://datamapper.org [7] http://www.sinatrarb.com [8] http://subforge.org/projects/list_files/subtle [9] http://www.selenic.com/mercurial [10] http://standards.freedesktop.org/basedir-spec/latest/ [11] http://subforge.org/wiki/subtle/Subtle [12] http://subforge.org/wiki/subtle/Sublets subtle-0.11.3224-xi/AUTHORS0000644000175000017500000000012711770332063015262 0ustar formorerformorerChristoph Kappel ICQ: 33679091, IRC: #subtle / irc.freenode.org subtle-0.11.3224-xi/src/0000755000175000017500000000000011770527221015003 5ustar formorerformorersubtle-0.11.3224-xi/src/subtlext/0000755000175000017500000000000011770527221016655 5ustar formorerformorersubtle-0.11.3224-xi/src/subtlext/client.c0000644000175000017500000007401311770332063020302 0ustar formorerformorer /** * @package subtlext * * @file Client functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/client.c,v 3216 2012/06/15 17:18:12 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* ClientRestack {{{ */ VALUE ClientRestack(VALUE self, int detail) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = 2; ///< Claim to be a pager data.l[1] = NUM2LONG(win); data.l[2] = detail; subSharedMessage(display, DefaultRootWindow(display), "_NET_RESTACK_WINDOW", data, 32, True); return self; } /* }}} */ /* ClientFlagsGet {{{ */ static VALUE ClientFlagsGet(VALUE self, int flag) { VALUE flags = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@flags", flags) flags = rb_iv_get(self, "@flags"); return (FIXNUM_P(flags) && FIX2INT(flags) & flag) ? Qtrue : Qfalse; } /* }}} */ /* ClientFlagsSet {{{ */ static VALUE ClientFlagsSet(VALUE self, int flag, int toggle) { int iflags = flag; VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Toggle flags */ if(toggle) { VALUE flags = Qnil; GET_ATTR(self, "@flags", flags); iflags = FIX2INT(flags); if(iflags & flag) iflags &= ~flag; else iflags |= flag; } /* Assemble message */ data.l[0] = NUM2LONG(win); data.l[1] = iflags; subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_CLIENT_FLAGS", data, 32, True); rb_iv_set(self, "@flags", INT2FIX(iflags)); return self; } /* }}} */ /* ClientFlagsToggle {{{ */ static VALUE ClientFlagsToggle(VALUE self, char *type, int flag) { int iflags = 0; VALUE win = Qnil, flags = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); GET_ATTR(self, "@flags", flags); subSubtlextConnect(NULL); ///< Implicit open connection /* Update flags */ iflags = FIX2INT(flags); if(iflags & flag) iflags &= ~flag; else iflags |= flag; rb_iv_set(self, "@flags", INT2FIX(iflags)); /* Send message */ data.l[0] = XInternAtom(display, "_NET_WM_STATE_TOGGLE", False); data.l[1] = XInternAtom(display, type, False); subSharedMessage(display, NUM2LONG(win), "_NET_WM_STATE", data, 32, True); return self; } /* }}} */ /* ClientGravity {{{ */ static int ClientGravity(VALUE key, VALUE value, VALUE client) { SubMessageData data = { .l = { -1, -1, -1, -1, -1 } }; data.l[0] = NUM2LONG(rb_iv_get(client, "@win")); /* Find gravity */ if(RTEST(value)) { VALUE gravity = Qnil; if(RTEST((gravity = subGravitySingFirst(Qnil, value)))) data.l[1] = FIX2INT(rb_iv_get(gravity, "@id")); } /* Find view id if any */ if(RTEST(key)) { VALUE view = Qnil; if(RTEST((view = subViewSingFirst(Qnil, key)))) data.l[2] = FIX2INT(rb_iv_get(view, "@id")); } /* Finally send message */ if(-1 != data.l[0] && -1 != data.l[1]) { subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_CLIENT_GRAVITY", data, 32, True); } return ST_CONTINUE; } /* }}} */ /* ClientFind {{{ */ static VALUE ClientFind(VALUE value, int first) { int flags = 0; VALUE parsed = Qnil; char buf[50] = { 0 }; subSubtlextConnect(NULL); ///< Implicit open connection /* Check object type */ switch(rb_type(parsed = subSubtlextParse( value, buf, sizeof(buf), &flags))) { case T_SYMBOL: if(CHAR2SYM("visible") == parsed) return subClientSingVisible(Qnil); else if(CHAR2SYM("all") == parsed) return subClientSingList(Qnil); else if(CHAR2SYM("current") == parsed) return subClientSingCurrent(Qnil); break; case T_OBJECT: if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("Client")))) return parsed; } return subSubtlextFindWindows("_NET_CLIENT_LIST", "Client", buf, flags, first); } /* }}} */ /* Singleton */ /* subClientSingSelect {{{ */ /* * call-seq: select -> Subtlext::Client or nil * * Click on a wondow and get the selected Client. * * Subtlext::Client.select * => # */ VALUE subClientSingSelect(VALUE self) { VALUE win = subSubtleSingSelect(self); return None != NUM2LONG(win) ? subClientSingFind(self, win) : Qnil; } /* }}} */ /* subClientSingFind {{{ */ /* * call-seq: find(value) -> Array * [value] -> Array * * Find Client by a given value which can be of following type: * * [Fixnum] Array index of the _NET_CLIENT_LIST property list. * [String] Regexp match against both WM_CLASS values. * [Hash] Instead of just match WM_CLASS match against * following properties: * * [:name] Match against WM_NAME * [:instance] Match against first value of WM_NAME * [:class] Match against second value of WM_NAME * [:gravity] Match against window Gravity * [:role] Match against WM_ROLE * [:pid] Match against window pid * * With one of following keys: :title, :name, :class, :gravity * [Symbol] Either :current for current View, :all for an * array of all Views or any string for an exact match. * * Subtlext::Client.find(1) * => [#] * * Subtlext::Client.find("subtle") * => [#] * * Subtlext::Client[".*"] * => [#, #] * * Subtlext::Client["subtle"] * => [] * * Subtlext::Client[:terms] * => [#] * * Subtlext::Client[name: "subtle"] * => [#] */ VALUE subClientSingFind(VALUE self, VALUE value) { return ClientFind(value, False); } /* }}} */ /* subClientSingFirst {{{ */ /* * call-seq: first(value) -> Subtlext::Client or nil * * Find first Client by a given value which can be of following type: * * [Fixnum] Array index of the _NET_CLIENT_LIST property list. * [String] Regexp match against both WM_CLASS values. * [Hash] Instead of just match WM_CLASS match against * following properties: * * [:name] Match against WM_NAME * [:instance] Match against first value of WM_NAME * [:class] Match against second value of WM_NAME * [:gravity] Match against window Gravity * [:role] Match against WM_ROLE * [:pid] Match against window pid * * With one of following keys: :title, :name, :class, :gravity * [Symbol] Either :current for current View, :all for an * array of all Views or any string for an exact match. * * Subtlext::Client.first(1) * => # * * Subtlext::Client.first("subtle") * => # * * Subtlext::Client.first(name: "subtle") * => # */ VALUE subClientSingFirst(VALUE self, VALUE value) { return ClientFind(value, True); } /* }}} */ /* subClientSingCurrent {{{ */ /* * call-seq: current -> Subtlext::Client * * Get current active Client. * * Subtlext::Client.current * => # */ VALUE subClientSingCurrent(VALUE self) { VALUE client = Qnil; unsigned long *focus = NULL; subSubtlextConnect(NULL); ///< Implicit open connection /* Get current client */ if((focus = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_WINDOW, XInternAtom(display, "_NET_ACTIVE_WINDOW", False), NULL))) { /* Update client values */ if(RTEST(client = subClientInstantiate(*focus))) subClientUpdate(client); free(focus); } else rb_raise(rb_eStandardError, "Invalid current window"); return client; } /* }}} */ /* subClientSingVisible {{{ */ /* * call-seq: visible -> Array * * Get array of all visible Client on all Views. * * Subtlext::Client.visible * => [#, #] * * Subtlext::Client.visible * => [] */ VALUE subClientSingVisible(VALUE self) { int i, nclients = 0; Window *clients = NULL; unsigned long *visible = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil, client = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); array = rb_ary_new(); klass = rb_const_get(mod, rb_intern("Client")); clients = subSubtlextWindowList("_NET_CLIENT_LIST", &nclients); visible = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_VISIBLE_TAGS", False), NULL); /* Check results */ if(clients && visible) { for(i = 0; i < nclients; i++) { unsigned long *tags = (unsigned long *)subSharedPropertyGet(display, clients[i], XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_TAGS", False), NULL); /* Create client on match */ if(tags && *tags && *visible & *tags && RTEST(client = rb_funcall(klass, meth, 1, LONG2NUM(clients[i])))) { subClientUpdate(client); rb_ary_push(array, client); } if(tags) free(tags); } } if(clients) free(clients); if(visible) free(visible); return array; } /* }}} */ /* subClientSingList {{{ */ /* * call-seq: list -> Array * * Get an array of all Clients based on _NET_CLIENT_LIST * property list. * * Subtlext::Client.list * => [#, #] * * Subtlext::Client.list * => [] */ VALUE subClientSingList(VALUE self) { int i, nclients = 0; Window *clients = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil, client = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); array = rb_ary_new(); klass = rb_const_get(mod, rb_intern("Client")); clients = subSubtlextWindowList("_NET_CLIENT_LIST", &nclients); /* Check results */ if(clients) { for(i = 0; i < nclients; i++) { /* Create client */ if(RTEST(client = rb_funcall(klass, meth, 1, LONG2NUM(clients[i])))) { subClientUpdate(client); rb_ary_push(array, client); } } free(clients); } return array; } /* }}} */ /* subClientSingRecent {{{ */ /* * call-seq: recent -> Array * * Get array of the last five active Clients. * * Subtlext::Client.recent * => [ # ] */ VALUE subClientSingRecent(VALUE self) { int i, nclients = 0; Window *clients = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil, client = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); array = rb_ary_new(); klass = rb_const_get(mod, rb_intern("Client")); clients = subSubtlextWindowList("_NET_ACTIVE_WINDOW", &nclients); /* Check results */ if(clients) { for(i = 0; i < nclients; i++) { /* Create client */ if(!NIL_P(client = rb_funcall(klass, meth, 1, LONG2NUM(clients[i])))) { subClientUpdate(client); rb_ary_push(array, client); } } free(clients); } return array; } /* }}} */ /* Helper */ /* subClientInstantiate {{{ */ VALUE subClientInstantiate(Window win) { VALUE klass = Qnil, client = Qnil; /* Create new instance */ klass = rb_const_get(mod, rb_intern("Client")); client = rb_funcall(klass, rb_intern("new"), 1, LONG2NUM(win)); return client; } /* }}} */ /* Class */ /* subClientInit {{{ */ /* * call-seq: new(win) -> Subtlext::Client * * Create a new Client object locally without calling #save automatically. * * The Client won't be visible until #save is called. * * client = Subtlext::Client.new(1) * => # */ VALUE subClientInit(VALUE self, VALUE win) { if(!FIXNUM_P(win)) rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(win)); /* Init object */ rb_iv_set(self, "@win", win); rb_iv_set(self, "@name", Qnil); rb_iv_set(self, "@instance", Qnil); rb_iv_set(self, "@klass", Qnil); rb_iv_set(self, "@role", Qnil); rb_iv_set(self, "@geometry", Qnil); rb_iv_set(self, "@gravity", Qnil); rb_iv_set(self, "@screen", Qnil); rb_iv_set(self, "@flags", Qnil); rb_iv_set(self, "@tags", Qnil); subSubtlextConnect(NULL); ///< Implicit open connection return self; } /* }}} */ /* subClientUpdate {{{ */ /* * call-seq: update -> Subtlext::Client * * Update Client properties based on required Client window id. * * client.update * => nil */ VALUE subClientUpdate(VALUE self) { Window win = None; rb_check_frozen(self); subSubtlextConnect(NULL); ///< Implicit open connection /* Check values */ if(0 <= (win = NUM2LONG(rb_iv_get(self, "@win")))) { int *tags = NULL, *flags = NULL; char *wmname = NULL, *wminstance = NULL, *wmclass = NULL, *role = NULL; /* Fetch name, instance and class */ subSharedPropertyClass(display, win, &wminstance, &wmclass); subSharedPropertyName(display, win, &wmname, wmclass); /* Fetch tags, flags and role */ tags = (int *)subSharedPropertyGet(display, win, XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_TAGS", False), NULL); flags = (int *)subSharedPropertyGet(display, win, XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_FLAGS", False), NULL); role = subSharedPropertyGet(display, win, XA_STRING, XInternAtom(display, "WM_WINDOW_ROLE", False), NULL); /* Set properties */ rb_iv_set(self, "@tags", tags ? INT2FIX(*tags) : INT2FIX(0)); rb_iv_set(self, "@flags", flags ? INT2FIX(*flags) : INT2FIX(0)); rb_iv_set(self, "@name", rb_str_new2(wmname)); rb_iv_set(self, "@instance", rb_str_new2(wminstance)); rb_iv_set(self, "@klass", rb_str_new2(wmclass)); rb_iv_set(self, "@role", role ? rb_str_new2(role) : Qnil); /* Set to nil for on demand loading */ rb_iv_set(self, "@geometry", Qnil); rb_iv_set(self, "@gravity", Qnil); if(tags) free(tags); if(flags) free(flags); if(role) free(role); free(wmname); free(wminstance); free(wmclass); } else rb_raise(rb_eStandardError, "Invalid client id `%#lx'", win); return self; } /* }}} */ /* subClientViewList {{{ */ /* * call-seq: views -> Array * * Get array of Views Client is visible. * * client.views * => [#, #] * * client.views * => nil */ VALUE subClientViewList(VALUE self) { int i, nnames = 0; char **names = NULL; VALUE win = Qnil, array = Qnil, method = Qnil, klass = Qnil; unsigned long *view_tags = NULL, *client_tags = NULL, *flags = NULL; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ method = rb_intern("new"); klass = rb_const_get(mod, rb_intern("View")); array = rb_ary_new(); names = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames); view_tags = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL); client_tags = (unsigned long *)subSharedPropertyGet(display, NUM2LONG(win), XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_TAGS", False), NULL); flags = (unsigned long *)subSharedPropertyGet(display, NUM2LONG(win), XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_FLAGS", False), NULL); /* Check results */ if(names && view_tags && client_tags) { for(i = 0; i < nnames; i++) { /* Check if there are common tags or window is stick */ if((view_tags[i] & *client_tags) || (flags && *flags & SUB_EWMH_STICK)) { /* Create new view */ VALUE v = rb_funcall(klass, method, 1, rb_str_new2(names[i])); rb_iv_set(v, "@id", INT2FIX(i)); rb_ary_push(array, v); } } } if(names) XFreeStringList(names); if(view_tags) free(view_tags); if(client_tags) free(client_tags); if(flags) free(flags); return array; } /* }}} */ /* subClientFlagsAskFull {{{ */ /* * call-seq: is_full? -> true or false * * Check if Client is fullscreen. * * client.is_full? * => true * * client.is_full? * => false */ VALUE subClientFlagsAskFull(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_FULL); } /* }}} */ /* subClientFlagsAskFloat {{{ */ /* * call-seq: is_float? -> true or false * * Check if Client is floating. * * client.is_float? * => true * * client.is_float? * => false */ VALUE subClientFlagsAskFloat(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_FLOAT); } /* }}} */ /* subClientFlagsAskStick {{{ */ /* * call-seq: is_stick? -> true or false * * Check if Client is sticky. * * client.is_stick? * => true * * client.is_stick? * => false */ VALUE subClientFlagsAskStick(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_STICK); } /* }}} */ /* subClientFlagsAskResize {{{ */ /* * call-seq: is_resize? -> true or false * * Check if Client uses size hints. * * client.is_resize? * => true * * client.is_resize? * => false */ VALUE subClientFlagsAskResize(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_RESIZE); } /* }}} */ /* subClientFlagsAskUrgent {{{ */ /* * call-seq: is_urgent? -> true or false * * Check if Client is urgent. * * client.is_urgent? * => true * * client.is_urgent? * => false */ VALUE subClientFlagsAskUrgent(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_URGENT); } /* }}} */ /* subClientFlagsAskZaphod {{{ */ /* * call-seq: is_zaphod? -> true or false * * Check if Client is zaphod. * * client.is_zaphod? * => true * * client.is_zaphod? * => false */ VALUE subClientFlagsAskZaphod(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_ZAPHOD); } /* }}} */ /* subClientFlagsAskFixed {{{ */ /* * call-seq: is_fixed? -> true or false * * Check if Client is fixed. * * client.is_fixed? * => true * * client.is_fixed? * => false */ VALUE subClientFlagsAskFixed(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_FIXED); } /* }}} */ /* subClientFlagsAskBorderless {{{ */ /* * call-seq: is_borderless? -> true or false * * Check if Client is borderless. * * client.is_borderless? * => true * * client.is_borderless? * => false */ VALUE subClientFlagsAskBorderless(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_BORDERLESS); } /* }}} */ /* subClientFlagsToggleFull {{{ */ /* * call-seq: toggle_full -> Subtlext::Client * * Toggle Client fullscreen state. * * client.toggle_full * => nil */ VALUE subClientFlagsToggleFull(VALUE self) { return ClientFlagsToggle(self, "_NET_WM_STATE_FULLSCREEN", SUB_EWMH_FULL); } /* }}} */ /* subClientFlagsToggleFloat {{{ */ /* * call-seq: toggle_float -> Subtlext::Client * * Toggle Client floating state. * * client.toggle_float * => nil */ VALUE subClientFlagsToggleFloat(VALUE self) { return ClientFlagsToggle(self, "_NET_WM_STATE_ABOVE", SUB_EWMH_FLOAT); } /* }}} */ /* subClientFlagsToggleStick {{{ */ /* * call-seq: toggle_stick -> Subtlext::Client * * Toggle Client sticky state. * * client.toggle_stick * => nil */ VALUE subClientFlagsToggleStick(VALUE self) { return ClientFlagsToggle(self, "_NET_WM_STATE_STICKY", SUB_EWMH_STICK); } /* }}} */ /* subClientFlagsToggleResize {{{ */ /* * call-seq: toggle_stick -> Subtlext::Client * * Toggle Client resize state. * * client.toggle_stick * => nil */ VALUE subClientFlagsToggleResize(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_RESIZE, True); } /* }}} */ /* subClientFlagsToggleUrgent {{{ */ /* * call-seq: toggle_urgent -> Subtlext::Client * * Toggle Client urgent state. * * client.toggle_urgent * => nil */ VALUE subClientFlagsToggleUrgent(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_URGENT, True); } /* }}} */ /* subClientFlagsToggleZaphod {{{ */ /* * call-seq: toggle_zaphod -> Subtlext::Client * * Toggle Client zaphod state. * * client.toggle_zaphod * => nil */ VALUE subClientFlagsToggleZaphod(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_ZAPHOD, True); } /* }}} */ /* subClientFlagsToggleFixed {{{ */ /* * call-seq: toggle_fixed -> Subtlext::Client * * Toggle Client fixed state. * * client.toggle_fixed * => nil */ VALUE subClientFlagsToggleFixed(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_FIXED, True); } /* }}} */ /* subClientFlagsToggleBorderless {{{ */ /* * call-seq: toggle_borderless -> Subtlext::Client * * Toggle Client borderless state. * * client.toggle_borderless * => nil */ VALUE subClientFlagsToggleBorderless(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_BORDERLESS, True); } /* }}} */ /* subClientFlagsWriter {{{ */ /* * call-seq: flags=(array) -> Subtlext::Client * * Set multiple flags at once. Flags can be one or a combination of the * following: * * [:full] Set fullscreen mode * [:float] Set floating mode * [:stick] Set sticky mode * [:resize] Set resize mode * [:urgent] Set urgent mode * [:zaphod] Set zaphod mode * [:fixed] Set fixed mode * [:borderless] Set borderless mode * * client.flags = [ :float, :stick ] * => nil */ VALUE subClientFlagsWriter(VALUE self, VALUE value) { /* Check object type */ if(T_ARRAY == rb_type(value)) { int i, flags = 0; VALUE entry = Qnil; /* Translate flags */ for(i = 0; Qnil != (entry = rb_ary_entry(value, i)); ++i) { if(CHAR2SYM("full") == entry) flags |= SUB_EWMH_FULL; else if(CHAR2SYM("float") == entry) flags |= SUB_EWMH_FLOAT; else if(CHAR2SYM("stick") == entry) flags |= SUB_EWMH_STICK; else if(CHAR2SYM("resize") == entry) flags |= SUB_EWMH_RESIZE; else if(CHAR2SYM("urgent") == entry) flags |= SUB_EWMH_URGENT; else if(CHAR2SYM("zaphod") == entry) flags |= SUB_EWMH_ZAPHOD; else if(CHAR2SYM("fixed") == entry) flags |= SUB_EWMH_FIXED; else if(CHAR2SYM("borderless") == entry) flags |= SUB_EWMH_BORDERLESS; } ClientFlagsSet(self, flags, False); } return self; } /* }}} */ /* subClientRestackRaise {{{ */ /* * call-seq: raise -> Subtlext::Client * * Move Client window to top of window stack. * * client.raise * => nil */ VALUE subClientRestackRaise(VALUE self) { return ClientRestack(self, Above); } /* }}} */ /* subClientRestackLower {{{ */ /* * call-seq: lower -> Subtlext::Client * * Move Client window to bottom of window stack. * * client.raise * => nil */ VALUE subClientRestackLower(VALUE self) { return ClientRestack(self, Below); } /* }}} */ /* subClientAskAlive {{{ */ /* * call-seq: alive? -> true or false * * Check if client is alive. * * client.alive? * => true * * client.alive? * => false */ VALUE subClientAskAlive(VALUE self) { VALUE ret = Qfalse, win = Qnil; XWindowAttributes attrs; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch client attributes */ if(!XGetWindowAttributes(display, NUM2LONG(win), &attrs)) rb_obj_freeze(self); else ret = Qtrue; return ret; } /* }}} */ /* subClientGravityReader {{{ */ /* * call-seq: gravity -> Subtlext::Gravity * * Get Client Gravity. * * client.gravity * => # */ VALUE subClientGravityReader(VALUE self) { VALUE win = Qnil, gravity = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subSubtlextConnect(NULL); ///< Implicit open connection /* Load on demand */ if(NIL_P((gravity = rb_iv_get(self, "@gravity")))) { int *id = NULL; char buf[5] = { 0 }; /* Get gravity */ if((id = (int *)subSharedPropertyGet(display, NUM2LONG(win), XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_GRAVITY", False), NULL))) { /* Create gravity */ snprintf(buf, sizeof(buf), "%d", *id); gravity = subGravityInstantiate(buf); subGravitySave(gravity); rb_iv_set(self, "@gravity", gravity); free(id); } } return gravity; } /* }}} */ /* subClientGravityWriter {{{ */ /* * call-seq: gravity=(fixnum) -> Fixnum * gravity=(symbol) -> Symbol * gravity=(object) -> Object * gravity=(hash) -> Hash * * Set Client Gravity either for current or for specific View. * * # Set gravity for current view * client.gravity = 0 * => # * * client.gravity = :center * => # * * client.gravity = Subtlext::Gravity[0] * => # * * # Set gravity for specific view * client.gravity = { :terms => :center } * => # */ VALUE subClientGravityWriter(VALUE self, VALUE value) { /* Check ruby object */ rb_check_frozen(self); subSubtlextConnect(NULL); ///< Implicit open connection /* Check value type */ switch(rb_type(value)) { case T_FIXNUM: case T_SYMBOL: case T_STRING: ClientGravity(Qnil, value, self); break; case T_OBJECT: if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("Gravity")))) ClientGravity(Qnil, value, self); break; case T_HASH: rb_hash_foreach(value, ClientGravity, self); break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } /* Reset gravity */ rb_iv_set(self, "@gravity", Qnil); return value; } /* }}} */ /* subClientGeometryReader {{{ */ /* * call-seq: geometry -> Subtlext::Geometry * * Get Client Geometry. * * client.geometry * => # */ VALUE subClientGeometryReader(VALUE self) { VALUE win = Qnil, geom = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subSubtlextConnect(NULL); ///< Implicit open connection /* Load on demand */ if(NIL_P((geom = rb_iv_get(self, "@geometry")))) { XRectangle geometry = { 0 }; subSharedPropertyGeometry(display, NUM2LONG(win), &geometry); geom = subGeometryInstantiate(geometry.x, geometry.y, geometry.width, geometry.height); rb_iv_set(self, "@geometry", geom); } return geom; } /* }}} */ /* subClientGeometryWriter {{{ */ /* * call-seq: geometry=(x, y, width, height) -> Fixnum * geometry=(array) -> Array * geometry=(hash) -> Hash * geometry=(string) -> String * geometry=(geometry) -> Subtlext::Geometry * * Set Client geometry. * * client.geometry = 0, 0, 100, 100 * => 0 * * client.geometry = [ 0, 0, 100, 100 ] * => [ 0, 0, 100, 100 ] * * client.geometry = { x: 0, y: 0, width: 100, height: 100 } * => { x: 0, y: 0, width: 100, height: 100 } * * client.geometry = "0x0+100+100" * => "0x0+100+100" * * client.geometry = Subtlext::Geometry(0, 0, 100, 100) * => # */ VALUE subClientGeometryWriter(int argc, VALUE *argv, VALUE self) { VALUE klass = Qnil, geom = Qnil; /* Check ruby object */ rb_check_frozen(self); subSubtlextConnect(NULL); ///< Implicit open connection /* Delegate arguments */ klass = rb_const_get(mod, rb_intern("Geometry")); geom = rb_funcall2(klass, rb_intern("new"), argc, argv); /* Update geometry */ if(RTEST(geom)) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; GET_ATTR(self, "@win", win); data.l[1] = FIX2INT(rb_iv_get(geom, "@x")); data.l[2] = FIX2INT(rb_iv_get(geom, "@y")); data.l[3] = FIX2INT(rb_iv_get(geom, "@width")); data.l[4] = FIX2INT(rb_iv_get(geom, "@height")); subSharedMessage(display, NUM2LONG(win), "_NET_MOVERESIZE_WINDOW", data, 32, True); rb_iv_set(self, "@geometry", geom); } return geom; } /* }}} */ /* subClientScreenReader {{{ */ /* * call-seq: screen -> Subtlext::Screen * * Get Client Screen. * * client.screen * => # */ VALUE subClientScreenReader(VALUE self) { VALUE screen = Qnil, win = Qnil; int *id = NULL; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Get screen */ if((id = (int *)subSharedPropertyGet(display, NUM2LONG(win), XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_SCREEN", False), NULL))) { screen = subScreenSingFind(self, INT2FIX(*id)); free(id); } return screen; } /* }}} */ /* subClientToString {{{ */ /* * call-seq: to_str -> String * * Convert this Client object to string. * * puts client * => "subtle" */ VALUE subClientToString(VALUE self) { VALUE name = Qnil; /* Check ruby object */ GET_ATTR(self, "@name", name); return name; } /* }}} */ /* subClientKill {{{ */ /* * call-seq: kill -> nil * * Send a close signal to Client and freeze this object. * * client.kill * => nil */ VALUE subClientKill(VALUE self) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = CurrentTime; data.l[1] = 2; ///< Claim to be a pager subSharedMessage(display, NUM2LONG(win), "_NET_CLOSE_WINDOW", data, 32, True); rb_obj_freeze(self); return Qnil; } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/color.c0000644000175000017500000002245611770332063020146 0ustar formorerformorer /** * @package subtlext * * @file Gravity functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/color.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" #define SCALE(i,div,mul) (unsigned long)(0 < i ? ((float)i / div) * mul : 0) /* ColorEqual {{{ */ VALUE ColorEqual(VALUE self, VALUE other, int check_type) { int ret = False; VALUE pixel1 = Qnil, pixel2 = Qnil; /* Check ruby object */ GET_ATTR(self, "@pixel", pixel1); GET_ATTR(other, "@pixel", pixel2); /* Check ruby object types */ if(check_type) { ret = (rb_obj_class(self) == rb_obj_class(other) && pixel1 == pixel2); } else ret = (pixel1 == pixel2); return ret ? Qtrue : Qfalse; } /* }}} */ /* ColorPixelToRGB {{{ */ static void ColorPixelToRGB(XColor *xcolor) { XQueryColor(display, DefaultColormap(display, DefaultScreen(display)), xcolor); /* Scale 65535 to 255 */ xcolor->red = SCALE(xcolor->red, 65535, 255); xcolor->green = SCALE(xcolor->green, 65535, 255); xcolor->blue = SCALE(xcolor->blue, 65535, 255); } /* }}} */ /* ColorRGBToPixel {{{ */ static void ColorRGBToPixel(XColor *xcolor) { /* Scale 255 to 65535 */ xcolor->red = SCALE(xcolor->red, 255, 65535); xcolor->green = SCALE(xcolor->green, 255, 65535); xcolor->blue = SCALE(xcolor->blue, 255, 65535); XAllocColor(display, DefaultColormap(display, DefaultScreen(display)), xcolor); /* Scale 65535 to 255 */ xcolor->red = SCALE(xcolor->red, 65535, 255); xcolor->green = SCALE(xcolor->green, 65535, 255); xcolor->blue = SCALE(xcolor->blue, 65535, 255); } /* }}} */ /* Helper */ /* subColorPixel {{{ */ unsigned long subColorPixel(VALUE red, VALUE green, VALUE blue, XColor *xcolor) { XColor xcol = { 0 }; /* Check object type */ switch(rb_type(red)) { case T_FIXNUM: case T_BIGNUM: if(NIL_P(green) && NIL_P(blue)) { xcol.pixel = NUM2LONG(red); ColorPixelToRGB(&xcol); } else { xcol.red = NUM2INT(red); xcol.green = NUM2INT(green); xcol.blue = NUM2INT(blue); ColorRGBToPixel(&xcol); } break; case T_STRING: xcol.pixel = subSharedParseColor(display, RSTRING_PTR(red)); ColorPixelToRGB(&xcol); break; case T_ARRAY: if(3 == FIX2INT(rb_funcall(red, rb_intern("size"), 0, NULL))) { xcol.red = NUM2INT(rb_ary_entry(red, 0)); xcol.green = NUM2INT(rb_ary_entry(red, 1)); xcol.blue = NUM2INT(rb_ary_entry(red, 2)); ColorRGBToPixel(&xcol); } break; case T_HASH: { xcol.red = NUM2INT(rb_hash_lookup(red, CHAR2SYM("red"))); xcol.green = NUM2INT(rb_hash_lookup(red, CHAR2SYM("green"))); xcol.blue = NUM2INT(rb_hash_lookup(red, CHAR2SYM("blue"))); ColorRGBToPixel(&xcol); } break; case T_OBJECT: { VALUE klass = rb_const_get(mod, rb_intern("Color")); /* Check object instance */ if(rb_obj_is_instance_of(red, klass)) { xcol.red = NUM2INT(rb_iv_get(red, "@red")); xcol.green = NUM2INT(rb_iv_get(red, "@green")); xcol.blue = NUM2INT(rb_iv_get(red, "@blue")); xcol.pixel = NUM2LONG(rb_iv_get(red, "@pixel")); } } break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(red)); } if(xcolor) { xcolor->red = xcol.red; xcolor->green = xcol.green; xcolor->blue = xcol.blue; xcolor->pixel = xcol.pixel; } return xcol.pixel; } /* }}} */ /* subColorInstantiate {{{ */ VALUE subColorInstantiate(unsigned long pixel) { VALUE klass = Qnil, color = Qnil; /* Create new instance */ klass = rb_const_get(mod, rb_intern("Color")); color = rb_funcall(klass, rb_intern("new"), 1, LONG2NUM(pixel)); return color; } /* }}} */ /* Class */ /* subColorInit {{{ */ /* * call-seq: new(red, green, blue) -> Subtlext::Color * new(string) -> Subtlext::Color * new(array) -> Subtlext::Color * new(hash) -> Subtlext::Color * new(fixnum) -> Subtlext::Color * new(color) -> Subtlext::Color * * Create new Color object from given value which can be of * following types: * * [String] Any color representation of Xlib is allowed * [Array] Must be an array with values for red, green and blue * [Hash] Must be a hash with values for red, green and blue * [Fixnum] Pixel representation of a color in Xlib * [Color] Copy color from a Color object * * Or just pass one argument for red, green or blue. * * color = Subtlext::Color.new(51, 102, 253) * => # * * color = Subtlext::Color.new("#336699") * => # * * color = Subtlext::Color.new("red") * => # * * color = Subtlext::Color.new([ 51, 102, 253 ]) * => # * * color = Subtlext::Color.new({ :red => 51, :green => 102, :blue => 253 ]) * => # * * color = Subtlext::Color.new(14253553) * => # * * color = Subtlext::Color.new(Subtlext::Color.new(51, 102, 253)) * => # */ VALUE subColorInit(int argc, VALUE *argv, VALUE self) { VALUE data[3] = { Qnil }; XColor xcolor = { 0 }; rb_scan_args(argc, argv, "12", &data[0], &data[1], &data[2]); subSubtlextConnect(NULL); ///< Implicit open connection /* Get color values */ subColorPixel(data[0], data[1], data[2], &xcolor); /* Set values */ rb_iv_set(self, "@red", INT2FIX(xcolor.red)); rb_iv_set(self, "@green", INT2FIX(xcolor.green)); rb_iv_set(self, "@blue", INT2FIX(xcolor.blue)); rb_iv_set(self, "@pixel", LONG2NUM(xcolor.pixel)); return self; } /* }}} */ /* subColorToHex {{{ */ /* * call-seq: to_hex -> String * * Convert this Color object to rrggbb hex string. * * puts color.to_hex * => "#ff0000" */ VALUE subColorToHex(VALUE self) { char buf[8] = { 0 }; VALUE red = Qnil, green = Qnil, blue = Qnil; /* Check ruby object */ GET_ATTR(self, "@red", red); GET_ATTR(self, "@green", green); GET_ATTR(self, "@blue", blue); snprintf(buf, sizeof(buf), "#%02X%02X%02X", (int)FIX2INT(red), (int)FIX2INT(green), (int)FIX2INT(blue)); return rb_str_new2(buf); } /* }}} */ /* subColorToArray {{{ */ /* * call-seq: to_a -> Array * * Convert this Color object to an array with one fixnum for red, blue * and green. * * color.to_a * => [ 51, 102, 253 ] */ VALUE subColorToArray(VALUE self) { VALUE ary = Qnil, red = Qnil, green = Qnil, blue = Qnil; /* Check ruby object */ GET_ATTR(self, "@red", red); GET_ATTR(self, "@green", green); GET_ATTR(self, "@blue", blue); /* Create new array */ ary = rb_ary_new2(3); /* Set values */ rb_ary_push(ary, red); rb_ary_push(ary, green); rb_ary_push(ary, blue); return ary; } /* }}} */ /* subColorToHash {{{ */ /* * call-seq: to_hash -> Hash * * Convert this Color object to a hash with one symbol/fixnum pair for * red, green and blue. * * color.to_hash * => { :red => 51, :green => 102, :blue => 253 } */ VALUE subColorToHash(VALUE self) { VALUE klass = Qnil, hash = Qnil, red = Qnil, green = Qnil, blue = Qnil; /* Check ruby object */ GET_ATTR(self, "@red", red); GET_ATTR(self, "@green", green); GET_ATTR(self, "@blue", blue); /* Create new hash */ klass = rb_const_get(rb_mKernel, rb_intern("Hash")); hash = rb_funcall(klass, rb_intern("new"), 0, NULL); /* Set values */ rb_hash_aset(hash, CHAR2SYM("red"), red); rb_hash_aset(hash, CHAR2SYM("green"), green); rb_hash_aset(hash, CHAR2SYM("blue"), blue); return hash; } /* }}} */ /* subColorToString {{{ */ /* * call-seq: to_str -> String * * Convert this Color object to string. * * puts color * => "<>123456789<>" */ VALUE subColorToString(VALUE self) { char buf[20] = { 0 }; VALUE pixel = Qnil; /* Check ruby object */ GET_ATTR(self, "@pixel", pixel); snprintf(buf, sizeof(buf), "%s#%ld%s", SEPARATOR, NUM2LONG(pixel), SEPARATOR); return rb_str_new2(buf); } /* }}} */ /* subColorOperatorPlus {{{ */ /* * call-seq: +(string) -> String * * Convert this Color to string and concat given string. * * color + "subtle" * => "<>123456789<>subtle" */ VALUE subColorOperatorPlus(VALUE self, VALUE value) { return subSubtlextConcat(subColorToString(self), value); } /* }}} */ /* subColorEqual {{{ */ /* * call-seq: ==(other) -> True or False * * Whether both objects have the same values (based on pixel). * * object1 == object2 * => true */ VALUE subColorEqual(VALUE self, VALUE other) { return ColorEqual(self, other, False); } /* }}} */ /* subColorEqualTyped {{{ */ /* * call-seq: eql?(other) -> True or False * * Whether both objects have the same values and types (based on pixel). * * object1.eql? object2 * => true */ VALUE subColorEqualTyped(VALUE self, VALUE other) { return ColorEqual(self, other, True); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/geometry.c0000644000175000017500000001726011770332063020660 0ustar formorerformorer /** * @package subtlext * * @file Geometry functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/geometry.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* GeometryEqual {{{ */ VALUE GeometryEqual(VALUE self, VALUE other) { int ret = False; /* Check ruby object types */ if(rb_obj_class(self) == rb_obj_class(other)) { XRectangle r1 = { 0 }, r2 = { 0 }; /* Get rectangles */ subGeometryToRect(self, &r1); subGeometryToRect(other, &r2); ret = (r1.x == r2.x && r1.y == r2.y && r1.width == r2.width && r1.height == r2.height); } return ret ? Qtrue : Qfalse; } /* }}} */ /* Helper */ /* subGeometryInstantiate {{{ */ VALUE subGeometryInstantiate(int x, int y, int width, int height) { VALUE klass = Qnil, geometry = Qnil; /* Create new instance */ klass = rb_const_get(mod, rb_intern("Geometry")); geometry = rb_funcall(klass, rb_intern("new"), 4, INT2FIX(x), INT2FIX(y), INT2FIX(width), INT2FIX(height)); return geometry; } /* }}} */ /* subGeometryToRect {{{ */ void subGeometryToRect(VALUE self, XRectangle *r) { /* Set values */ r->x = FIX2INT(rb_iv_get(self, "@x")); r->y = FIX2INT(rb_iv_get(self, "@y")); r->width = FIX2INT(rb_iv_get(self, "@width")); r->height = FIX2INT(rb_iv_get(self, "@height")); } /* }}} */ /* Class */ /* subGeometryInit {{{ */ /* * call-seq: new(x, y, width, height) -> Subtlext::Geometry * new(array) -> Subtlext::Geometry * new(hash) -> Subtlext::Geometry * new(string) -> Subtlext::Geometry * new(geometry) -> Subtlext::Geometry * * Create a new Geometry object from givem value which can be of * following types: * * [Array] Must be an array with values for x, y, width and height * [Hash] Must be a hash with values for x, y, width and height * [String] Must be a string of following format: XxY+WIDTH+HEIGHT * [Geometry] Copy geometry from a Geometry object * * Or just pass one argument for x, y, width and height. * * Creating a geometry with zero width or height will raise * a StandardError. * * geom = Subtlext::Geometry.new(0, 0, 50, 50) * => # * * geom = Subtlext::Geometry.new([ 0, 0, 50, 50 ]) * => # * * geom = Subtlext::Geometry.new({ :x => 0, :y => 0, :width => 50, :height => 50 }) * => # * * geom = Subtlext::Geometry.new("0x0+100+100") * => # * * geom = Subtlext::Geometry.new(Subtlext::Geometry.new(0, 0, 50, 50)) * => # */ VALUE subGeometryInit(int argc, VALUE *argv, VALUE self) { VALUE value = Qnil, data[4] = { Qnil }; rb_scan_args(argc, argv, "13", &data[0], &data[1], &data[2], &data[3]); value = data[0]; /* Check object type */ switch(rb_type(value)) { case T_FIXNUM: break; case T_ARRAY: if(4 == FIX2INT(rb_funcall(value, rb_intern("size"), 0, NULL))) { int i; for(i = 0; 4 > i; i++) data[i] = rb_ary_entry(value, i); } break; case T_HASH: { int i; const char *syms[] = { "x", "y", "width", "height" }; for(i = 0; 4 > i; i++) data[i] = rb_hash_lookup(value, CHAR2SYM(syms[i])); } break; case T_STRING: { XRectangle geom = { 0 }; sscanf(RSTRING_PTR(value), "%hdx%hd+%hu+%hu", &geom.x, &geom.y, &geom.width, &geom.height); /* Convert values */ data[0] = INT2FIX(geom.x); data[1] = INT2FIX(geom.y); data[2] = INT2FIX(geom.width); data[3] = INT2FIX(geom.height); } break; case T_OBJECT: { VALUE klass = rb_const_get(mod, rb_intern("Geometry")); /* Check object instance */ if(rb_obj_is_instance_of(value, klass)) { data[0] = rb_iv_get(value, "@x"); data[1] = rb_iv_get(value, "@y"); data[2] = rb_iv_get(value, "@width"); data[3] = rb_iv_get(value, "@height"); } } break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } /* Set values */ if(FIXNUM_P(data[0]) && FIXNUM_P(data[1]) && FIXNUM_P(data[2]) && FIXNUM_P(data[3]) && 0 < FIX2INT(data[2]) && 0 < FIX2INT(data[3])) { rb_iv_set(self, "@x", data[0]); rb_iv_set(self, "@y", data[1]); rb_iv_set(self, "@width", data[2]); rb_iv_set(self, "@height", data[3]); } else rb_raise(rb_eStandardError, "Invalid geometry"); return self; } /* }}} */ /* subGeometryToArray {{{ */ /* * call-seq: to_a -> Array * * Convert this Geometry object to an array with one fixnum for x, y, width * and height. * * geom.to_a * => [0, 0, 50, 50] */ VALUE subGeometryToArray(VALUE self) { VALUE ary = Qnil, x = Qnil, y = Qnil, width = Qnil, height = Qnil; /* Check ruby object */ GET_ATTR(self, "@x", x); GET_ATTR(self, "@y", y); GET_ATTR(self, "@width", width); GET_ATTR(self, "@height", height); /* Create new array */ ary = rb_ary_new2(4); /* Set values */ rb_ary_push(ary, x); rb_ary_push(ary, y); rb_ary_push(ary, width); rb_ary_push(ary, height); return ary; } /* }}} */ /* subGeometryToHash {{{ */ /* * call-seq: to_hash -> Hash * * Convert this Geometry object to hash with one symbol/fixnum pair for * x, y, height and width. * * geom.to_hash * => { :x => 0, :y => 0, :width => 50, :height => 50 } */ VALUE subGeometryToHash(VALUE self) { VALUE klass = Qnil, hash = Qnil; VALUE x = Qnil, y = Qnil, width = Qnil, height = Qnil; /* Check ruby object */ GET_ATTR(self, "@x", x); GET_ATTR(self, "@y", y); GET_ATTR(self, "@width", width); GET_ATTR(self, "@height", height); /* Create new hash */ klass = rb_const_get(rb_mKernel, rb_intern("Hash")); hash = rb_funcall(klass, rb_intern("new"), 0, NULL); /* Set values */ rb_hash_aset(hash, CHAR2SYM("x"), x); rb_hash_aset(hash, CHAR2SYM("y"), y); rb_hash_aset(hash, CHAR2SYM("width"), width); rb_hash_aset(hash, CHAR2SYM("height"), height); return hash; } /* }}} */ /* subGeometryToString {{{ */ /* * call-seq: to_str -> String * * Convert this Geometry object to string. * * puts geom * => "0x0+50+50" */ VALUE subGeometryToString(VALUE self) { char buf[256] = { 0 }; VALUE x = Qnil, y = Qnil, width = Qnil, height = Qnil; /* Check ruby object */ GET_ATTR(self, "@x", x); GET_ATTR(self, "@y", y); GET_ATTR(self, "@width", width); GET_ATTR(self, "@height", height); snprintf(buf, sizeof(buf), "%dx%d+%d+%d", (int)FIX2INT(x), (int)FIX2INT(y), (int)FIX2INT(width), (int)FIX2INT(height)); return rb_str_new2(buf); } /* }}} */ /* subGeometryEqual {{{ */ /* * call-seq: ==(other) -> True or False * * Whether both objects have the same values (based on geometry). * * object1 == object2 * => true */ VALUE subGeometryEqual(VALUE self, VALUE other) { return GeometryEqual(self, other); } /* }}} */ /* subGeometryEqualTyped {{{ */ /* * call-seq: eql?(other) -> True or False * * Whether both objects have the same values and types (based on geometry). * * object1.eql? object2 * => true */ VALUE subGeometryEqualTyped(VALUE self, VALUE other) { return GeometryEqual(self, other); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/view.c0000644000175000017500000004030111770332063017767 0ustar formorerformorer /** * @package subtle * * @file subtle ruby extension * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/view.c,v 3216 2012/06/15 17:18:12 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* ViewSelect {{{ */ static VALUE ViewSelect(VALUE self, int dir) { int nnames = 0; char **names = NULL; VALUE id = Qnil, ret = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ if((names = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames))) { int vid = FIX2INT(id); /* Select view */ if(SUB_VIEW_NEXT == dir && vid < (nnames - 1)) { vid++; } else if(SUB_VIEW_PREV == dir && 0 < vid) { vid--; } /* Create view */ ret = subViewInstantiate(names[vid]); subViewUpdate(ret); XFreeStringList(names); } return ret; } /* }}} */ /* ViewFind {{{ */ static VALUE ViewFind(VALUE value, int first) { int flags = 0; VALUE parsed = Qnil; char buf[50] = { 0 }; subSubtlextConnect(NULL); ///< Implicit open connection /* Check object type */ switch(rb_type(parsed = subSubtlextParse( value, buf, sizeof(buf), &flags))) { case T_SYMBOL: if(CHAR2SYM("visible") == parsed) return subViewSingVisible(Qnil); else if(CHAR2SYM("all") == parsed) return subViewSingList(Qnil); else if(CHAR2SYM("current") == parsed) return subViewSingCurrent(Qnil); break; case T_OBJECT: if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("View")))) return parsed; } return subSubtlextFindObjects("_NET_DESKTOP_NAMES", "View", buf, flags, first); } /* }}} */ /* Singleton */ /* subViewSingFind {{{ */ /* * call-seq: find(value) -> Array * [value] -> Array * * Find View by a given value which can be of following type: * * [Fixnum] Array index of the _NET_DESKTOP_NAMES property list. * [String] Regexp match against name of Views, returns a View on single * match or an Array on multiple matches. * [Symbol] Either :current for current View, :all for an * array of all Views or any string for an exact match. * * Subtlext::View.find(1) * => [#] * * Subtlext::View.find("subtle") * => [#] * * Subtlext::View[".*"] * => [#, #] * * Subtlext::View["subtle"] * => [] * * Subtlext::View[:terms] * => #] */ VALUE subViewSingFind(VALUE self, VALUE value) { return ViewFind(value, False); } /* }}} */ /* subViewSingFirst {{{ */ /* * call-seq: first(value) -> Subtlext::View or nil * * Find first View by a given value which can be of following type: * * [Fixnum] Array index of the _NET_DESKTOP_NAMES property list. * [String] Regexp match against name of Views, returns a View on single * match or an Array on multiple matches. * [Symbol] Either :current for current View, :all for an * array of all Views or any string for an exact match. * * Subtlext::View.first(1) * => # * * Subtlext::View.first("subtle") * => # */ VALUE subViewSingFirst(VALUE self, VALUE value) { return ViewFind(value, True); } /* }}} */ /* subViewSingCurrent {{{ */ /* * call-seq: current -> Subtlext::View * * Get currently active View. * * Subtlext::View.current * => # */ VALUE subViewSingCurrent(VALUE self) { int nnames = 0; char **names = NULL; unsigned long *cur_view = NULL; VALUE view = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ names = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames); cur_view = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "_NET_CURRENT_DESKTOP", False), NULL); /* Check results */ if(names && cur_view) { /* Create instance */ view = subViewInstantiate(names[*cur_view]); rb_iv_set(view, "@id", INT2FIX(*cur_view)); } if(names) XFreeStringList(names); if(cur_view) free(cur_view); return view; } /* }}} */ /* subViewSingVisible {{{ */ /* * call-seq: visible -> Array * * Get an array of all visible Views on connected Screens. * * Subtlext::View.visible * => [#, #] * * Subtlext::View.visible * => [] */ VALUE subViewSingVisible(VALUE self) { int i, nnames = 0, *tags = NULL; char **names = NULL; unsigned long *visible = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil, v = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); klass = rb_const_get(mod, rb_intern("View")); array = rb_ary_new(); names = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames); visible = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_VISIBLE_VIEWS", False), NULL); tags = (int *)subSharedPropertyGet(display, ROOT, XA_CARDINAL, XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL); /* Check results */ if(names && visible && tags) { for(i = 0; i < nnames; i++) { /* Create view on match */ if(*visible & (1L << (i + 1)) && !NIL_P(v = rb_funcall(klass, meth, 1, rb_str_new2(names[i])))) { rb_iv_set(v, "@id", INT2FIX(i)); rb_iv_set(v, "@tags", INT2FIX(tags[i])); rb_ary_push(array, v); } } } if(names) XFreeStringList(names); if(visible) free(visible); if(tags) free(tags); return array; } /* }}} */ /* subViewSingList {{{ */ /* * call-seq: list -> Array * * Get an array of all Views based on the _NET_DESKTOP_NAMES * property list. * * Subtlext::View.list * => [#, #] * * Subtlext::View.list * => [] */ VALUE subViewSingList(VALUE self) { int i, nnames = 0; long *tags = NULL; char **names = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil, v = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ klass = rb_const_get(mod, rb_intern("View")); meth = rb_intern("new"); array = rb_ary_new(); names = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames); tags = (long *)subSharedPropertyGet(display, ROOT, XA_CARDINAL, XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL); /* Check results */ if(names && tags) { for(i = 0; i < nnames; i++) { if(!NIL_P(v = rb_funcall(klass, meth, 1, rb_str_new2(names[i])))) { rb_iv_set(v, "@id", INT2FIX(i)); rb_iv_set(v, "@tags", LONG2NUM(tags[i])); rb_ary_push(array, v); } } } if(names) XFreeStringList(names); if(tags) free(tags); return array; } /* }}} */ /* Helper */ /* subViewInstantiate {{{ */ VALUE subViewInstantiate(char *name) { VALUE klass = Qnil, view = Qnil; assert(name); /* Create new instance */ klass = rb_const_get(mod, rb_intern("View")); view = rb_funcall(klass, rb_intern("new"), 1, rb_str_new2(name)); return view; } /* }}} */ /* Class */ /* subViewInit {{{ */ /* * call-seq: new(name) -> Subtlext::View * * Create a new View object locally without calling #save automatically. * * The View won't be visible until #save is called. * * view = Subtlext::View.new("subtle") * => # */ VALUE subViewInit(VALUE self, VALUE name) { if(T_STRING != rb_type(name)) rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(name)); /* Init object */ rb_iv_set(self, "@id", Qnil); rb_iv_set(self, "@name", name); rb_iv_set(self, "@tags", Qnil); subSubtlextConnect(NULL); ///< Implicit open connection return self; } /* }}} */ /* subViewUpdate {{{ */ /* * call-seq: update -> Subtlext::View * * Update View properties based on required View index. * * view.update * => # */ VALUE subViewUpdate(VALUE self) { long *tags = NULL, ntags = 0; VALUE id = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch tags */ if((tags = (long *)subSharedPropertyGet(display, ROOT, XA_CARDINAL, XInternAtom(display, "SUBTLE_VIEW_TAGS", False), (unsigned long *)&ntags))) { int idx = FIX2INT(id); rb_iv_set(self, "@tags", LONG2NUM(idx < ntags ? tags[idx] : 0)); free(tags); } return self; } /* }}} */ /* subViewSave {{{ */ /* * call-seq: save -> Subtlext::View * * Save new View object. * * view.save * => # */ VALUE subViewSave(VALUE self) { int id = -1; VALUE name = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@name", name); subSubtlextConnect(NULL); ///< Implicit open connection /* Create view if needed */ if(-1 == (id = subSubtlextFindString("_NET_DESKTOP_NAMES", RSTRING_PTR(name), NULL, SUB_MATCH_EXACT))) { SubMessageData data = { { 0, 0, 0, 0, 0 } }; snprintf(data.b, sizeof(data.b), "%s", RSTRING_PTR(name)); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_VIEW_NEW", data, 8, True); id = subSubtlextFindString("_NET_DESKTOP_NAMES", RSTRING_PTR(name), NULL, SUB_MATCH_EXACT); } /* Guess view id */ if(-1 == id) { int nnames = 0; char **names = NULL; /* Get names of views */ if((names = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames))) { id = nnames; ///< New id should be last if(names) XFreeStringList(names); } } /* Set properties */ rb_iv_set(self, "@id", INT2FIX(id)); return self; } /* }}} */ /* subViewClients {{{ */ /* * call-seq: clients -> Array * * Get an array of visible Clients on this View. * * view.clients * => [#, #] * * view.clients * => [] */ VALUE subViewClients(VALUE self) { int i, nclients = 0; Window *clients = NULL; VALUE id = Qnil, klass = Qnil, meth = Qnil, array = Qnil, client = Qnil; unsigned long *view_tags = NULL; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ klass = rb_const_get(mod, rb_intern("Client")); meth = rb_intern("new"); array = rb_ary_new(); clients = subSubtlextWindowList("_NET_CLIENT_LIST", &nclients); view_tags = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL); /* Check results */ if(clients && view_tags) { for(i = 0; i < nclients; i++) { unsigned long *client_tags = NULL, *flags = NULL; /* Fetch window data */ client_tags = (unsigned long *)subSharedPropertyGet(display, clients[i], XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_TAGS", False), NULL); flags = (unsigned long *)subSharedPropertyGet(display, clients[i], XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_FLAGS", False), NULL); /* Check if there are common tags or window is stick */ if((client_tags && view_tags[FIX2INT(id)] & *client_tags) || (flags && *flags & SUB_EWMH_STICK)) { if(RTEST(client = rb_funcall(klass, meth, 1, LONG2NUM(clients[i])))) { subClientUpdate(client); rb_ary_push(array, client); } } if(client_tags) free(client_tags); if(flags) free(flags); } } if(clients) free(clients); if(view_tags) free(view_tags); return array; } /* }}} */ /* subViewJump {{{ */ /* * call-seq: jump -> Subtlext::View * * Set this View to the current active one * * view.jump * => # */ VALUE subViewJump(VALUE self) { VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = FIX2INT(id); data.l[2] = -1; subSharedMessage(display, DefaultRootWindow(display), "_NET_CURRENT_DESKTOP", data, 32, True); return self; } /* }}} */ /* subViewSelectNext {{{ */ /* * call-seq: next -> Subtlext::View or nil * * Select next View, but doesn't cycle list. * * view.next * => # */ VALUE subViewSelectNext(VALUE self) { return ViewSelect(self, SUB_VIEW_NEXT); } /* }}} */ /* subViewSelectPrev {{{ */ /* * call-seq: prev -> Subtlext::View or nil * * Select prev View, but doesn't cycle list. * * view.prev * => # */ VALUE subViewSelectPrev(VALUE self) { return ViewSelect(self, SUB_VIEW_PREV); } /* }}} */ /* subViewAskCurrent {{{ */ /* * call-seq: current? -> true or false * * Check if this View is the current active View. * * view.current? * => true * * view.current? * => false */ VALUE subViewAskCurrent(VALUE self) { VALUE id = Qnil, ret = Qfalse;; unsigned long *cur_view = NULL; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); /* Check results */ if((cur_view = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "_NET_CURRENT_DESKTOP", False), NULL))) { if(FIX2INT(id) == *cur_view) ret = Qtrue; free(cur_view); } return ret; } /* }}} */ /* subViewIcon {{{ */ /* * call-seq: icon -> Subtlext::Icon or nil * * Get the Icon of the View. * * view.icon * => # */ VALUE subViewIcon(VALUE self) { unsigned long nicons = 0; VALUE id = Qnil, ret = Qnil; unsigned long *icons = NULL; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Check results */ if((icons = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_VIEW_ICONS", False), &nicons))) { int iid = FIX2INT(id); /* Check if id is in range and icon available */ if(0 <= iid && iid < nicons && -1 != icons[iid]) { /* Create new icon */ ret = rb_funcall(rb_const_get(mod, rb_intern("Icon")), rb_intern("new"), 1, LONG2NUM(icons[iid])); } free(icons); } return ret; } /* }}} */ /* subViewToString {{{ */ /* * call-seq: to_str -> String * * Convert this View object to string. * * puts view * => "subtle" */ VALUE subViewToString(VALUE self) { VALUE name = Qnil; /* Check ruby object */ GET_ATTR(self, "@name", name); return name; } /* }}} */ /* subViewKill {{{ */ /* * call-seq: kill -> nil * * Remove this View from subtle and freeze this object. * * view.kill * => nil */ VALUE subViewKill(VALUE self) { VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = FIX2INT(id); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_VIEW_KILL", data, 32, True); rb_obj_freeze(self); ///< Freeze object return Qnil; } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/tray.c0000644000175000017500000001576311770332063020012 0ustar formorerformorer /** * @package subtle * * @file subtle ruby extension * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/tray.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* TrayFind {{{ */ static VALUE TrayFind(VALUE value, int first) { int flags = 0; VALUE parsed = Qnil; char buf[50] = { 0 }; subSubtlextConnect(NULL); ///< Implicit open connection /* Check object type */ switch(rb_type(parsed = subSubtlextParse( value, buf, sizeof(buf), &flags))) { case T_SYMBOL: if(CHAR2SYM("all") == parsed) return subTraySingList(Qnil); break; case T_OBJECT: if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("Tray")))) return parsed; } return subSubtlextFindWindows("SUBTLE_TRAY_LIST", "Tray", buf, flags, first); } /* }}} */ /* Singleton */ /* subTraySingFind {{{ */ /* * call-seq: find(value) -> Array * [value] -> Array * * Find Tray by a given value which can be of following type: * * [Fixnum] Array index of the SUBTLE_TRAY_LIST property list. * [String] Regexp match against both WM_CLASS values. * [Hash] Instead of just match WM_CLASS match against * following properties: * * [:name] Match against WM_NAME * [:instance] Match against first value of WM_NAME * [:class] Match against second value of WM_NAME * [Symbol] Either :all for an array of all Trays or any string for * an exact match. * Subtlext::Tray.find(1) * => [#] * * Subtlext::Tray.find("subtle") * => [#] * * Subtlext::Tray[".*"] * => [#, #] * * Subtlext::Tray["subtle"] * => [] * * Subtlext::Tray[:terms] * => [#] * * Subtlext::Tray[name: "subtle"] * => [#] */ VALUE subTraySingFind(VALUE self, VALUE value) { return TrayFind(value, False); } /* }}} */ /* subTraySingFirst {{{ */ /* * call-seq: find(value) -> Subtlext::Tray or nil * * Find first Tray by a given value which can be of following type: * * [Fixnum] Array index of the SUBTLE_TRAY_LIST property list. * [String] Regexp match against both WM_CLASS values. * [Hash] Instead of just match WM_CLASS match against * following properties: * * [:name] Match against WM_NAME * [:instance] Match against first value of WM_NAME * [:class] Match against second value of WM_NAME * [Symbol] Either :all for an array of all Trays or any string for * an exact match. * Subtlext::Tray.first(1) * => # * * Subtlext::Tray.first("subtle") * => # */ VALUE subTraySingFirst(VALUE self, VALUE value) { return TrayFind(value, True); } /* }}} */ /* subTraySingList {{{ */ /* * call-seq: list -> Array * * Get an array of all Trays based on SUBTLE_TRAY_LIST * property list. * * Subtlext::Tray.list * => [#, #] * * Subtlext::Tray.list * => [] */ VALUE subTraySingList(VALUE self) { int i, ntrays = 0; Window *trays = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); klass = rb_const_get(mod, rb_intern("Tray")); array = rb_ary_new(); /* Check results */ if((trays = subSubtlextWindowList("SUBTLE_TRAY_LIST", &ntrays))) { for(i = 0; i < ntrays; i++) { VALUE t = rb_funcall(klass, meth, 1, LONG2NUM(trays[i])); if(!NIL_P(t)) subTrayUpdate(t); rb_ary_push(array, t); } free(trays); } return array; } /* }}} */ /* Helper */ /* subTrayInstantiate {{{ */ VALUE subTrayInstantiate(Window win) { VALUE klass = Qnil, tray = Qnil; /* Create new instance */ klass = rb_const_get(mod, rb_intern("Tray")); tray = rb_funcall(klass, rb_intern("new"), 1, LONG2NUM(win)); return tray; } /* }}} */ /* Class */ /* subTrayInit {{{ */ /* * call-seq: new(name) -> Subtlext::Tray * * Create a new Tray object locally without calling #save automatically. * * The Tray won't be visible until #save is called. * * tag = Subtlext::Tray.new("subtle") * => # */ VALUE subTrayInit(VALUE self, VALUE win) { if(!FIXNUM_P(win) && T_BIGNUM != rb_type(win)) rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(win)); /* Init object */ rb_iv_set(self, "@win", win); rb_iv_set(self, "@name", Qnil); rb_iv_set(self, "@klass", Qnil); rb_iv_set(self, "@title", Qnil); subSubtlextConnect(NULL); ///< Implicit open connection return self; } /* }}} */ /* subTrayUpdate {{{ */ /* * call-seq: update -> Subtlext::Tray * * Update Tray properties based on required Tray window id. * * tray.update * => # */ VALUE subTrayUpdate(VALUE self) { Window win = None; /* Check ruby object */ rb_check_frozen(self); subSubtlextConnect(NULL); ///< Implicit open connection /* Get tray values */ win = NUM2LONG(rb_iv_get(self, "@win")); /* Check values */ if(0 <= win) { char *wmname = NULL, *wminstance = NULL, *wmclass = NULL; /* Get name, instance and class */ subSharedPropertyClass(display, win, &wminstance, &wmclass); subSharedPropertyName(display, win, &wmname, wmclass); /* Set properties */ rb_iv_set(self, "@name", rb_str_new2(wmname)); rb_iv_set(self, "@instance", rb_str_new2(wminstance)); rb_iv_set(self, "@klass", rb_str_new2(wmclass)); free(wmname); free(wminstance); free(wmclass); } else rb_raise(rb_eStandardError, "Invalid tray id `%#lx`", win); return self; } /* }}} */ /* subTrayToString {{{ */ /* * call-seq: to_str -> String * * Convert this Tray object to string. * * puts tray * => "subtle" */ VALUE subTrayToString(VALUE self) { VALUE name = Qnil; /* Check ruby object */ GET_ATTR(self, "@name", name); return name; } /* }}} */ /* subTrayKill {{{ */ /* * call-seq: kill -> nil * * Send a close signal to Client and freeze this object. * * tray.kill * => nil */ VALUE subTrayKill(VALUE self) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = CurrentTime; data.l[1] = 2; ///< Claim to be a pager subSharedMessage(display, NUM2LONG(win), "_NET_CLOSE_WINDOW", data, 32, True); rb_obj_freeze(self); return Qnil; } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/tag.c0000644000175000017500000002564111770332063017602 0ustar formorerformorer /** * @package subtle * * @file subtle ruby extension * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/tag.c,v 3216 2012/06/15 17:18:12 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* TagFind {{{ */ static VALUE TagFind(VALUE value, int first) { int flags = 0; VALUE parsed = Qnil; char buf[50] = { 0 }; subSubtlextConnect(NULL); ///< Implicit open connection /* Check object type */ switch(rb_type(parsed = subSubtlextParse( value, buf, sizeof(buf), &flags))) { case T_SYMBOL: if(CHAR2SYM("visible") == parsed) return subTagSingVisible(Qnil); else if(CHAR2SYM("all") == parsed) return subTagSingList(Qnil); break; case T_OBJECT: if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("Tag")))) return parsed; } return subSubtlextFindObjects("SUBTLE_TAG_LIST", "Tag", buf, flags, first); } /* }}} */ /* Singleton */ /* subTagSingFind {{{ */ /* * call-seq: find(value) -> Array * [value] -> Array * * Find Tag by a given value which can be of following type: * * [Fixnum] Array index of the SUBTLE_TAG_LIST property list. * [String] Regexp match against name of Tags, returns a Tag on single * match or an Array on multiple matches. * [Symbol] Either :all for an array of all Tags or any string for * an exact match. * * Subtlext::Tag.find(1) * => [#] * * Subtlext::Tag.find("subtle") * => [#] * * Subtlext::Tag[".*"] * => [#, #] * * Subtlext::Tag["subtle"] * => [] * * Subtlext::Tag[:terms] * => [#] */ VALUE subTagSingFind(VALUE self, VALUE value) { return TagFind(value, False); } /* }}} */ /* subTagSingFirst {{{ */ /* * call-seq: first(value) -> Subtlext::Tag or nil * * Find first Tag by a given value which can be of following type: * * [Fixnum] Array index of the SUBTLE_TAG_LIST property list. * [String] Regexp match against name of Tags, returns a Tag on single * match or an Array on multiple matches. * [Symbol] Either :all for an array of all Tags or any string for * an exact match. * * Subtlext::Tag.first(1) * => # * * Subtlext::Tag.first("subtle") * => # */ VALUE subTagSingFirst(VALUE self, VALUE value) { return TagFind(value, True); } /* }}} */ /* subTagSingVisible {{{ */ /* * call-seq: visible -> Array * * Get an array of all visible Tags on all Views. * * Subtlext::Tag.visible * => [#, #] * * Subtlext::Tag.visible * => [] */ VALUE subTagSingVisible(VALUE self) { int i, ntags = 0; char **tags = NULL; unsigned long *visible = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil, t = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); klass = rb_const_get(mod, rb_intern("Tag")); array = rb_ary_new(); tags = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "SUBTLE_TAG_LIST", False), &ntags); visible = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_VISIBLE_TAGS", False), NULL); /* Populate array */ if(tags && visible) { for(i = 0; i < ntags; i++) { /* Create tag on match */ if(*visible & (1L << (i + 1)) && !NIL_P(t = rb_funcall(klass, meth, 1, rb_str_new2(tags[i])))) { rb_iv_set(t, "@id", INT2FIX(i)); rb_ary_push(array, t); } } } if(tags) XFreeStringList(tags); if(visible) free(visible); return array; } /* }}} */ /* subTagSingList {{{ */ /* * call-seq: list -> Array * * Get an array of all Tags based on the SUBTLE_TAG_LIST * property list. * * Subtlext::Tag.list * => [#, #] * * Subtlext::Tag.list * => [] */ VALUE subTagSingList(VALUE self) { int i, ntags = 0; char **tags = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); klass = rb_const_get(mod, rb_intern("Tag")); array = rb_ary_new(); /* Check results */ if((tags = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "SUBTLE_TAG_LIST", False), &ntags))) { for(i = 0; i < ntags; i++) { VALUE t = rb_funcall(klass, meth, 1, rb_str_new2(tags[i])); rb_iv_set(t, "@id", INT2FIX(i)); rb_ary_push(array, t); } XFreeStringList(tags); } return array; } /* }}} */ /* Helper */ /* subTagInstantiate {{{ */ VALUE subTagInstantiate(char *name) { VALUE klass = Qnil, tag = Qnil; /* Create new instance */ klass = rb_const_get(mod, rb_intern("Tag")); tag = rb_funcall(klass, rb_intern("new"), 1, rb_str_new2(name)); return tag; } /* }}} */ /* Class */ /* subTagInit {{{ */ /* * call-seq: new(name) -> Subtlext::Tag * * Create new Tag object locally without calling #save automatically. * * The Tag won't be useable until #save is called. * * tag = Subtlext::Tag.new("subtle") * => # */ VALUE subTagInit(VALUE self, VALUE name) { if(T_STRING != rb_type(name)) rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(name)); /* Init object */ rb_iv_set(self, "@id", Qnil); rb_iv_set(self, "@name", name); subSubtlextConnect(NULL); ///< Implicit open connection return self; } /* }}} */ /* subTagSave {{{ */ /* * call-seq: save -> Subtlext::Tag * * Save new Tag object. * * tag.update * => # */ VALUE subTagSave(VALUE self) { int id = -1; VALUE name = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@name", name); subSubtlextConnect(NULL); ///< Implicit open connection /* Create tag if needed */ if(-1 == (id = subSubtlextFindString("SUBTLE_TAG_LIST", RSTRING_PTR(name), NULL, SUB_MATCH_EXACT))) { SubMessageData data = { { 0, 0, 0, 0, 0 } }; snprintf(data.b, sizeof(data.b), "%s", RSTRING_PTR(name)); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_TAG_NEW", data, 8, True); id = subSubtlextFindString("SUBTLE_TAG_LIST", RSTRING_PTR(name), NULL, SUB_MATCH_EXACT); } /* Guess tag id */ if(-1 == id) { int ntags = 0; char **tags = NULL; /* Get names of tags */ if((tags = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "SUBTLE_TAG_LIST", False), &ntags))) { id = ntags; ///< New id should be last XFreeStringList(tags); } } /* Set properties */ rb_iv_set(self, "@id", INT2FIX(id)); return self; } /* }}} */ /* subTagClients {{{ */ /* * call-seq: clients -> Array * * Get an rray of Clients that are tagged with this Tag. * * tag.clients * => [#, #] * * tag.clients * => [] */ VALUE subTagClients(VALUE self) { int i, nclients = 0; Window *clients = NULL; unsigned long *tags = NULL; VALUE id = Qnil, array = Qnil, klass = Qnil, meth = Qnil, c = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); /* Fetch data */ klass = rb_const_get(mod, rb_intern("Client")); meth = rb_intern("new"); array = rb_ary_new(); clients = subSubtlextWindowList("_NET_CLIENT_LIST", &nclients); /* Check results */ if(clients) { for(i = 0; i < nclients; i++) { if((tags = (unsigned long *)subSharedPropertyGet(display, clients[i], XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_TAGS", False), NULL))) { /* Check if tag id matches */ if(*tags & (1L << (FIX2INT(id) + 1))) { /* Create new client */ if(!NIL_P(c = rb_funcall(klass, meth, 1, LONG2NUM(clients[i])))) { subClientUpdate(c); rb_ary_push(array, c); } } } } free(clients); } return array; } /* }}} */ /* subTagViews {{{ */ /* * call-seq: views -> Array * * Get an rray of Views that are tagged with this Tag. * * tag.views * => [#, #] * * tag.views * => [] */ VALUE subTagViews(VALUE self) { int i, nnames = 0; char **names = NULL; unsigned long *tags = NULL; VALUE id = Qnil, array = Qnil, klass = Qnil, meth = Qnil, v = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ klass = rb_const_get(mod, rb_intern("View")); meth = rb_intern("new"); array = rb_ary_new(); names = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames); tags = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL); /* Check results */ if(names && tags) { for(i = 0; i < nnames; i++) { /* Check if tag id matches */ if(tags[i] & (1L << (FIX2INT(id) + 1))) { /* Create new view */ if(!NIL_P(v = rb_funcall(klass, meth, 1, rb_str_new2(names[i])))) { rb_iv_set(v, "@id", INT2FIX(i)); rb_ary_push(array, v); } } } } if(names) XFreeStringList(names); if(tags) free(tags); return array; } /* }}} */ /* subTagToString {{{ */ /* * call-seq: to_str -> String * * Convert this Tag object to string. * * puts tag * => "subtle" */ VALUE subTagToString(VALUE self) { VALUE name = Qnil; /* Check ruby object */ GET_ATTR(self, "@name", name); return name; } /* }}} */ /* subTagKill {{{ */ /* * call-seq: kill -> nil * * Remove this Tag from subtle and freeze this object. * * tag.kill * => nil */ VALUE subTagKill(VALUE self) { VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = FIX2INT(id); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_TAG_KILL", data, 32, True); rb_obj_freeze(self); ///< Freeze object return Qnil; } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/icon.c0000644000175000017500000004272011770332063017754 0ustar formorerformorer /** * @package subtlext * * @file Gravity functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/icon.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include "subtlext.h" #ifdef HAVE_WORDEXP_H #include #endif /* HAVE_WORDEXP_H */ #ifdef HAVE_X11_XPM_H #include #endif /* HAVE_X11_XPM_H */ /* Flags {{{ */ #define ICON_BITMAP (1L << 0) #define ICON_PIXMAP (1L << 1) #define ICON_FOREIGN (1L << 2) /* }}} */ /* Typedef {{{ */ typedef struct subtlexticon_t { GC gc; Pixmap pixmap; int flags; unsigned int width, height; VALUE instance; } SubtlextIcon; /* }}} */ /* IconMark {{{ */ static void IconMark(SubtlextIcon *i) { if(i) rb_gc_mark(i->instance); } /* }}} */ /* IconSweep {{{ */ static void IconSweep(SubtlextIcon *i) { if(i) { /* Check if we can kill the pixmap here */ if(!(i->flags & ICON_FOREIGN) && i->pixmap) XFreePixmap(display, i->pixmap); if(0 != i->gc) XFreeGC(display, i->gc); free(i); } } /* }}} */ /* IconEqual {{{ */ VALUE IconEqual(VALUE self, VALUE other) { int ret = False; /* Check ruby object types */ if(rb_obj_class(self) == rb_obj_class(other)) { SubtlextIcon *i1 = NULL, *i2 = NULL; /* Get icons */ Data_Get_Struct(self, SubtlextIcon, i1); Data_Get_Struct(other, SubtlextIcon, i2); ret = (i1 && i2 && i1->width == i2->width && i1->height == i2->height); } return ret ? Qtrue : Qfalse; } /* }}} */ /* Class */ /* subIconAlloc {{{ */ /* * call-seq: new(path) -> Subtlext::Icon * new(width, height) -> Subtlext::Icon * * Allocate space for a new Icon object. */ VALUE subIconAlloc(VALUE self) { SubtlextIcon *i = NULL; /* Create icon */ i = (SubtlextIcon *)subSharedMemoryAlloc(1, sizeof(SubtlextIcon)); i->instance = Data_Wrap_Struct(self, IconMark, IconSweep, (void *)i); return i->instance; } /* }}} */ /* subIconInit {{{ */ /* * call-seq: initialize(path) -> Subtlext::Icon * initialize(width, height, bitmap) -> Subtlext::Icon * * Initialize Icon object. * * icon = Subtlext::Icon.new("/path/to/icon") * => # * * icon = Subtlext::Icon.new(8, 8) * => # */ VALUE subIconInit(int argc, VALUE *argv, VALUE self) { SubtlextIcon *i = NULL; Data_Get_Struct(self, SubtlextIcon, i); if(i) { VALUE data[3] = { Qnil }; rb_scan_args(argc, argv, "12", &data[0], &data[1], &data[2]); subSubtlextConnect(NULL); ///< Implicit open connection /* Find or create icon */ if(T_STRING == rb_type(data[0])) ///< Icon path { int hotx = 0, hoty = 0; char buf[100] = { 0 }; #ifdef HAVE_WORDEXP_H /* Expand tildes in path */ wordexp_t we; if(0 == wordexp(RSTRING_PTR(data[0]), &we, 0)) { snprintf(buf, sizeof(buf), "%s", we.we_wordv[0]); wordfree(&we); } else #endif /* HAVE_WORDEXP_H */ snprintf(buf, sizeof(buf), "%s", RSTRING_PTR(data[0])); /* Find file */ if(-1 == access(buf, R_OK)) { char *home = NULL; /* Combine paths */ if((home = getenv("XDG_DATA_HOME"))) { snprintf(buf, sizeof(buf), "%s/subtle/icons/%s", home, RSTRING_PTR(data[0])); } else { snprintf(buf, sizeof(buf), "%s/.local/share/subtle/icons/%s", getenv("HOME"), RSTRING_PTR(data[0])); } /* Check if icon exists */ if(-1 == access(buf, R_OK)) rb_raise(rb_eStandardError, "Invalid icon `%s'", RSTRING_PTR(data[0])); } /* Reading bitmap or pixmap icon file */ if(BitmapSuccess != XReadBitmapFile(display, DefaultRootWindow(display), buf, &i->width, &i->height, &i->pixmap, &hotx, &hoty)) { #ifdef HAVE_X11_XPM_H XpmAttributes attrs; /* We could define a color to use on transparent areas, but * this can be done on init only, so we just ignore it and * expect pixmaps to have no transparent areas at all */ /* Init attributes */ attrs.colormap = DefaultColormap(display, DefaultScreen(display)); attrs.depth = DefaultDepth(display, DefaultScreen(display)); attrs.visual = DefaultVisual(display, DefaultScreen(display)); attrs.valuemask = XpmColormap|XpmDepth|XpmVisual; if(XpmSuccess == XpmReadFileToPixmap(display, DefaultRootWindow(display), buf, &i->pixmap, NULL, &attrs)) { i->flags |= ICON_PIXMAP; i->width = attrs.width; i->height = attrs.height; } else #endif /* HAVE_X11_XPM_H */ { rb_raise(rb_eStandardError, "Invalid icon data"); return Qnil; } } else i->flags |= ICON_BITMAP; } else if(FIXNUM_P(data[0]) && FIXNUM_P(data[1])) ///< Icon dimensions { int depth = 1; /* Create pixmap or bitmap */ if(Qtrue == data[2]) { i->flags |= ICON_PIXMAP; depth = XDefaultDepth(display, DefaultScreen(display)); } else i->flags |= ICON_BITMAP; /* Create empty pixmap */ i->width = FIX2INT(data[0]); i->height = FIX2INT(data[1]); i->pixmap = XCreatePixmap(display, DefaultRootWindow(display), i->width, i->height, depth); } else if(FIXNUM_P(data[0])) { XRectangle geom = { 0 }; i->flags |= (ICON_BITMAP|ICON_FOREIGN); i->pixmap = NUM2LONG(data[0]); subSharedPropertyGeometry(display, i->pixmap, &geom); i->width = geom.width; i->height = geom.height; } else rb_raise(rb_eArgError, "Unexpected value-types"); /* Update object */ rb_iv_set(i->instance, "@width", INT2FIX(i->width)); rb_iv_set(i->instance, "@height", INT2FIX(i->height)); rb_iv_set(i->instance, "@pixmap", LONG2NUM(i->pixmap)); XSync(display, False); ///< Sync all changes } return Qnil; } /* }}} */ /* subIconDrawPoint {{{ */ /* * call-seq: draw_point(x, y, fg, bg) -> Subtlext::Icon * * Draw a pixel on the icon at given coordinates in given colors. * * icon.draw_point(1, 1) * => # * * icon.draw_point(1, 1, "#ff0000", "#000000") * => # */ VALUE subIconDrawPoint(int argc, VALUE *argv, VALUE self) { VALUE data[4] = { Qnil }; rb_scan_args(argc, argv, "22", &data[0], &data[1], &data[2], &data[3]); /* Check object types */ if(FIXNUM_P(data[0]) && FIXNUM_P(data[1])) { SubtlextIcon *i = NULL; Data_Get_Struct(self, SubtlextIcon, i); if(i) { XGCValues gvals; /* Create on demand */ if(0 == i->gc) i->gc = XCreateGC(display, i->pixmap, 0, NULL); /* Update GC */ gvals.foreground = 1; gvals.background = 0; if(i->flags & ICON_PIXMAP) { if(!NIL_P(data[2])) gvals.foreground = subColorPixel(data[2], Qnil, Qnil, NULL); if(!NIL_P(data[3])) gvals.background = subColorPixel(data[3], Qnil, Qnil, NULL); } XChangeGC(display, i->gc, GCForeground|GCBackground, &gvals); XDrawPoint(display, i->pixmap, i->gc, FIX2INT(data[0]), FIX2INT(data[1])); XFlush(display); } } else rb_raise(rb_eArgError, "Unexpected value-types"); return self; } /* }}} */ /* subIconDrawLine {{{ */ /* * call-seq: draw_line(x1, y1, x2, y2, fg, bg) -> Subtlext::Icon * * Draw a line on the Icon starting at x1/y1 to x2/y2 in given colors. * * icon.draw_line(1, 1, 10, 1) * => # * * icon.draw_line(1, 1, 10, 1, "#ff0000", "#000000") * => # */ VALUE subIconDrawLine(int argc, VALUE *argv, VALUE self) { VALUE data[6] = { Qnil }; rb_scan_args(argc, argv, "42", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]); /* Check object types */ if(FIXNUM_P(data[0]) && FIXNUM_P(data[1]) && FIXNUM_P(data[2]) && FIXNUM_P(data[3])) { SubtlextIcon *i = NULL; Data_Get_Struct(self, SubtlextIcon, i); if(i) { XGCValues gvals; /* Create on demand */ if(0 == i->gc) i->gc = XCreateGC(display, i->pixmap, 0, NULL); /* Update GC */ gvals.foreground = 1; gvals.background = 0; if(i->flags & ICON_PIXMAP) { if(!NIL_P(data[4])) gvals.foreground = subColorPixel(data[4], Qnil, Qnil, NULL); if(!NIL_P(data[5])) gvals.background = subColorPixel(data[5], Qnil, Qnil, NULL); } XChangeGC(display, i->gc, GCForeground|GCBackground, &gvals); XDrawLine(display, i->pixmap, i->gc, FIX2INT(data[0]), FIX2INT(data[1]), FIX2INT(data[2]), FIX2INT(data[3])); XFlush(display); } } else rb_raise(rb_eArgError, "Unexpected value-types"); return self; } /* }}} */ /* subIconDrawRect {{{ */ /* * call-seq: draw_rect(x, y, width, height, fill, fg, bg) -> Subtlext::Icon * * Draw a rect on the Icon starting at x/y with given width, height * and colors. * * icon.draw_rect(1, 1, 10, 10, false) * => # * * icon.draw_rect(1, 1, 10, 10, false, "#ff0000", "#000000") * => # */ VALUE subIconDrawRect(int argc, VALUE *argv, VALUE self) { VALUE data[7] = { Qnil }; rb_scan_args(argc, argv, "43", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6]); /* Check object types */ if(FIXNUM_P(data[0]) && FIXNUM_P(data[1]) && FIXNUM_P(data[2]) && FIXNUM_P(data[3])) { SubtlextIcon *i = NULL; Data_Get_Struct(self, SubtlextIcon, i); if(i) { XGCValues gvals; /* Create on demand */ if(0 == i->gc) i->gc = XCreateGC(display, i->pixmap, 0, NULL); /* Update GC */ gvals.foreground = 1; gvals.background = 0; if(i->flags & ICON_PIXMAP) { if(!NIL_P(data[5])) gvals.foreground = subColorPixel(data[5], Qnil, Qnil, NULL); if(!NIL_P(data[6])) gvals.background = subColorPixel(data[6], Qnil, Qnil, NULL); } XChangeGC(display, i->gc, GCForeground|GCBackground, &gvals); /* Draw rect */ if(Qtrue == data[4]) { XFillRectangle(display, i->pixmap, i->gc, FIX2INT(data[0]), FIX2INT(data[1]), FIX2INT(data[2]), FIX2INT(data[3])); } else XDrawRectangle(display, i->pixmap, i->gc, FIX2INT(data[0]), FIX2INT(data[1]), FIX2INT(data[2]), FIX2INT(data[3])); XFlush(display); } } else rb_raise(rb_eArgError, "Unexpected value-types"); return self; } /* }}} */ /* subIconCopyArea {{{ */ /* * call-seq: copy_area(icon2, src_x, src_y, width, height, dest_x, dest_y) -> Subtlext::Icon * * Copy area of given width/height from one Icon to another. * * icon.copy_area(icon, 0, 0, 10, 10, 0, 0) * => # */ VALUE subIconCopyArea(int argc, VALUE *argv, VALUE self) { VALUE data[7] = { Qnil }; rb_scan_args(argc, argv, "16", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6]); /* Check object type */ if(rb_obj_is_instance_of(data[0], rb_const_get(mod, rb_intern("Icon")))) { SubtlextIcon *src = NULL, *dest = NULL; Data_Get_Struct(data[0], SubtlextIcon, src); Data_Get_Struct(self, SubtlextIcon, dest); if(src && dest) { int src_x = 0, src_y = 0, dest_x = 0, dest_y = 0; int iwidth = 0, iheight = 0, area_w = 0, area_h = 0; VALUE width = Qnil, height = Qnil; /* Get icon dimesions */ GET_ATTR(self, "@width", width); GET_ATTR(self, "@height", height); iwidth = FIX2INT(width); iheight = FIX2INT(height); /* Check args */ if(!NIL_P(data[1])) src_x = FIX2INT(data[1]); if(!NIL_P(data[2])) src_y = FIX2INT(data[2]); if(!NIL_P(data[3])) area_w = FIX2INT(data[3]); if(!NIL_P(data[4])) area_h = FIX2INT(data[4]); if(!NIL_P(data[5])) dest_x = FIX2INT(data[5]); if(!NIL_P(data[6])) dest_y = FIX2INT(data[6]); /* Sanitize args */ if(0 == area_w) area_w = iwidth; if(0 == area_h) area_h = iheight; if(area_w > dest_x + iwidth) area_w = iwidth - dest_x; if(area_h > dest_y + iheight) area_h = iheight - dest_y; if(0 > src_x || src_x > iwidth) src_x = 0; if(0 > src_y || src_y > iheight) src_y = 0; if(0 > dest_x || dest_x > iwidth) dest_x = 0; if(0 > dest_y || dest_y > iheight) dest_y = 0; /* Create on demand */ if(0 == dest->gc) dest->gc = XCreateGC(display, dest->pixmap, 0, NULL); /* Copy area */ if(src->flags & ICON_PIXMAP && dest->flags & ICON_PIXMAP) { XCopyPlane(display, src->pixmap, dest->pixmap, dest->gc, src_x, src_y, area_w, area_h, dest_x, dest_y, 1); } else XCopyArea(display, src->pixmap, dest->pixmap, dest->gc, src_x, src_y, area_w, area_h, dest_x, dest_y); XFlush(display); } } else rb_raise(rb_eArgError, "Unexpected value-types"); return self; } /* }}} */ /* subIconClear {{{ */ /* * call-seq: clear -> Subtlext::Icon * clear(fg, bg) -> Subtlext::Icon * * Clear the icon and optionally set a fore-/background color. * * icon.clear * => # * * icon.clear("#ff0000", "#000000") * => # */ VALUE subIconClear(int argc, VALUE *argv, VALUE self) { SubtlextIcon *i = NULL; Data_Get_Struct(self, SubtlextIcon, i); if(i) { XGCValues gvals; if(0 == i->gc) ///< Create on demand i->gc = XCreateGC(display, i->pixmap, 0, NULL); /* Update GC */ gvals.foreground = 0; gvals.background = 1; if(i->flags & ICON_PIXMAP) { VALUE colors[2] = { Qnil }; rb_scan_args(argc, argv, "02", &colors[0], &colors[1]); if(!NIL_P(colors[0])) gvals.foreground = subColorPixel(colors[0], Qnil, Qnil, NULL); if(!NIL_P(colors[1])) gvals.background = subColorPixel(colors[1], Qnil, Qnil, NULL); } XChangeGC(display, i->gc, GCForeground|GCBackground, &gvals); XFillRectangle(display, i->pixmap, i->gc, 0, 0, i->width, i->height); XFlush(display); } return self; } /* }}} */ /* subIconAskBitmap {{{ */ /* * call-seq: bitmap? -> true or false * * Whether icon is a bitmap. * * icon.bitmap? * => true * * icon.bitmap? * => false */ VALUE subIconAskBitmap(VALUE self) { VALUE ret = Qfalse; SubtlextIcon *i = NULL; Data_Get_Struct(self, SubtlextIcon, i); if(i) ret = (i->flags & ICON_BITMAP) ? Qtrue : Qfalse; return ret; } /* }}} */ /* subIconToString {{{ */ /* * call-seq: to_str -> String * * Convert this Icon object to string. * * puts icon * => "<>!4<>" */ VALUE subIconToString(VALUE self) { VALUE ret = Qnil; SubtlextIcon *i = NULL; Data_Get_Struct(self, SubtlextIcon, i); if(i) { char buf[20] = { 0 }; snprintf(buf, sizeof(buf), "%s%c%ld%s", SEPARATOR, i->flags & ICON_PIXMAP ? '&' : '!', i->pixmap, SEPARATOR); ret = rb_str_new2(buf); } return ret; } /* }}} */ /* subIconOperatorPlus {{{ */ /* * call-seq: +(string) -> String * * Convert this Icon to string and concat given string. * * icon + "subtle" * => "<>!4<>subtle" */ VALUE subIconOperatorPlus(VALUE self, VALUE value) { return subSubtlextConcat(subIconToString(self), value); } /* }}} */ /* subIconOperatorMult {{{ */ /* * call-seq: *(value) -> String * * Convert this Icon to string and concat it multiple times. * * icon * 2 * => "<>!4<><>!4<>" */ VALUE subIconOperatorMult(VALUE self, VALUE value) { VALUE ret = Qnil; /* Check id and name */ if(FIXNUM_P(value)) { /* Passthru to string class */ ret = rb_funcall(subIconToString(self), rb_intern("*"), 1, value); } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); return ret; } /* }}} */ /* subIconEqual {{{ */ /* * call-seq: ==(other) -> True or False * * Whether both objects have the same values (based on geometry). * * object1 == object2 * => true */ VALUE subIconEqual(VALUE self, VALUE other) { return IconEqual(self, other); } /* }}} */ /* subIconEqualTyped {{{ */ /* * call-seq: eql?(other) -> True or False * * Whether both objects have the same values and types (based on geometry). * * object1.eql? object2 * => true */ VALUE subIconEqualTyped(VALUE self, VALUE other) { return IconEqual(self, other); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/subtlext.h0000644000175000017500000004570311770332063020707 0ustar formorerformorer /** * @package subtlext * * @file Header file * @copyright Copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/subtlext.h,v 3216 2012/06/15 17:18:12 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #ifndef SUBTLEXT_H #define SUBTLEXT_H 1 /* Includes {{{ */ #include #include "shared.h" /* }}} */ /* Macros {{{ */ #define CHAR2SYM(name) ID2SYM(rb_intern(name)) #define SYM2CHAR(sym) rb_id2name(SYM2ID(sym)) #define GET_ATTR(owner,name,value) \ if(NIL_P(value = rb_iv_get(owner, name))) return Qnil; #define ROOT DefaultRootWindow(display) /* }}} */ /* Flags {{{ */ #define SUB_TYPE_CLIENT 0 ///< Client #define SUB_TYPE_GRAVITY 1 ///< Gravity #define SUB_TYPE_VIEW 2 ///< View #define SUB_TYPE_TAG 3 ///< Tag #define SUB_TYPE_TRAY 4 ///< Tray #define SUB_TYPE_SCREEN 5 ///< Screen #define SUB_TYPE_SUBLET 6 ///< Sublet /* }}} */ /* Typedefs {{{ */ extern Display *display; extern VALUE mod; #ifdef DEBUG extern int debug; #endif /* DEBUG */ /* }}} */ /* client.c {{{ */ /* Singleton */ VALUE subClientSingSelect(VALUE self); ///< Select client VALUE subClientSingFind(VALUE self, VALUE value); ///< Find client VALUE subClientSingFirst(VALUE self, VALUE value); ///< Find first client VALUE subClientSingCurrent(VALUE self); ///< Get current client VALUE subClientSingVisible(VALUE self); ///< Get all visible clients VALUE subClientSingList(VALUE self); ///< Get all clients VALUE subClientSingRecent(VALUE self); ///< Get recent active clients /* Class */ VALUE subClientInstantiate(Window win); ///< Instantiate client VALUE subClientInit(VALUE self, VALUE win); ///< Create client VALUE subClientUpdate(VALUE self); ///< Update client VALUE subClientViewList(VALUE self); ///< Get views clients is on VALUE subClientFlagsAskFull(VALUE self); ///< Is client fullscreen VALUE subClientFlagsAskFloat(VALUE self); ///< Is client floating VALUE subClientFlagsAskStick(VALUE self); ///< Is client stick VALUE subClientFlagsAskResize(VALUE self); ///< Is client resize VALUE subClientFlagsAskUrgent(VALUE self); ///< Is client urgent VALUE subClientFlagsAskZaphod(VALUE self); ///< Is client zaphod VALUE subClientFlagsAskFixed(VALUE self); ///< Is client fixed VALUE subClientFlagsAskBorderless(VALUE self); ///< Is client borderless VALUE subClientFlagsToggleFull(VALUE self); ///< Toggle fullscreen VALUE subClientFlagsToggleFloat(VALUE self); ///< Toggle floating VALUE subClientFlagsToggleStick(VALUE self); ///< Toggle stick VALUE subClientFlagsToggleResize(VALUE self); ///< Toggle resize VALUE subClientFlagsToggleUrgent(VALUE self); ///< Toggle urgent VALUE subClientFlagsToggleZaphod(VALUE self); ///< Toggle zaphod VALUE subClientFlagsToggleFixed(VALUE self); ///< Toggle fixed VALUE subClientFlagsToggleBorderless(VALUE self); ///< Toggle borderless VALUE subClientFlagsWriter(VALUE self, VALUE value); ///< Set multiple flags VALUE subClientRestackRaise(VALUE self); ///< Raise client VALUE subClientRestackLower(VALUE self); ///< Lower client VALUE subClientAskAlive(VALUE self); ///< Is client alive VALUE subClientGravityReader(VALUE self); ///< Get client gravity VALUE subClientGravityWriter(VALUE self, VALUE value); ///< Set client gravity VALUE subClientGeometryReader(VALUE self); ///< Get client geometry VALUE subClientGeometryWriter(int argc, VALUE *argv, VALUE self); ///< Set client geometry VALUE subClientScreenReader(VALUE self); ///< Get client screen VALUE subClientResizeWriter(VALUE self, VALUE value); ///< Set Client resize VALUE subClientToString(VALUE self); ///< Client to string VALUE subClientKill(VALUE self); ///< Kill client /* }}} */ /* color.c {{{ */ unsigned long subColorPixel(VALUE red, VALUE green, VALUE blue, XColor *xcolor); ///< Get pixel value VALUE subColorInstantiate(unsigned long pixel); ///< Instantiate color VALUE subColorInit(int argc, VALUE *argv, VALUE self); ///< Create new color VALUE subColorToHex(VALUE self); ///< Convert to hex string VALUE subColorToArray(VALUE self); ///< Color to array VALUE subColorToHash(VALUE self); ///< Color to hash VALUE subColorToString(VALUE self); ///< Convert to string VALUE subColorOperatorPlus(VALUE self, VALUE value); ///< Concat string VALUE subColorEqual(VALUE self, VALUE other); ///< Whether objects are equal VALUE subColorEqualTyped(VALUE self, VALUE other); ///< Whether objects are equal typed /* }}} */ /* geometry.c {{{ */ VALUE subGeometryInstantiate(int x, int y, int width, int height); ///< Instantiate geometry void subGeometryToRect(VALUE self, XRectangle *r); ///< Geometry to rect VALUE subGeometryInit(int argc, VALUE *argv, VALUE self); ///< Create new geometry VALUE subGeometryToArray(VALUE self); ///< Geometry to array VALUE subGeometryToHash(VALUE self); ///< Geometry to hash VALUE subGeometryToString(VALUE self); ///< Geometry to string VALUE subGeometryEqual(VALUE self, VALUE other); ///< Whether objects are equal VALUE subGeometryEqualTyped(VALUE self, VALUE other); ///< Whether objects are equal typed /* }}} */ /* gravity.c {{{ */ /* Singleton */ VALUE subGravitySingFind(VALUE self, VALUE value); ///< Find gravity VALUE subGravitySingFirst(VALUE self, VALUE value); ///< Find first gravity VALUE subGravitySingList(VALUE self); ///< Get all gravities /* Class */ VALUE subGravityInstantiate(char *name); ///< Instantiate gravity VALUE subGravityInit(int argc, VALUE *argv, VALUE self); ///< Create new gravity VALUE subGravitySave(VALUE self); ///< Save gravity VALUE subGravityClients(VALUE self); ///< List clients with gravity VALUE subGravityGeometryFor(VALUE self, VALUE value); ///< Get geometry gravity for screen VALUE subGravityGeometryReader(VALUE self); ///< Get geometry gravity VALUE subGravityGeometryWriter(int argc, VALUE *argv, VALUE self);///< Get geometry gravity VALUE subGravityTilingWriter(VALUE self, VALUE value); ///< Set gravity tiling VALUE subGravityToString(VALUE self); ///< Gravity to string VALUE subGravityToSym(VALUE self); ///< Gravity to symbol VALUE subGravityKill(VALUE self); ///< Kill gravity /* }}} */ /* icon.c {{{ */ VALUE subIconAlloc(VALUE self); ///< Allocate icon VALUE subIconInit(int argc, VALUE *argv, VALUE self); ///< Init icon VALUE subIconDrawPoint(int argc, VALUE *argv, VALUE self); ///< Draw a point VALUE subIconDrawLine(int argc, VALUE *argv, VALUE self); ///< Draw a line VALUE subIconDrawRect(int argc, VALUE *argv, VALUE self); ///< Draw a rect VALUE subIconCopyArea(int argc, VALUE *argv, VALUE self); ///< Copy icon area VALUE subIconClear(int argc, VALUE *argv, VALUE self); ///< Clear icon VALUE subIconAskBitmap(VALUE self); ///< Whether icon is bitmap VALUE subIconToString(VALUE self); ///< Convert to string VALUE subIconOperatorPlus(VALUE self, VALUE value); ///< Concat string VALUE subIconOperatorMult(VALUE self, VALUE value); ///< Concat string VALUE subIconEqual(VALUE self, VALUE other); ///< Whether objects are equal VALUE subIconEqualTyped(VALUE self, VALUE other); ///< Whether objects are equal typed /* }}} */ /* screen.c {{{ */ /* Singleton */ VALUE subScreenSingFind(VALUE self, VALUE id); ///< Find screen VALUE subScreenSingList(VALUE self); ///< Get all screens VALUE subScreenSingCurrent(VALUE self); ///< Get current screen /* Class */ VALUE subScreenInstantiate(int id); ///< Instantiate screen VALUE subScreenInit(VALUE self, VALUE id); ///< Create new screen VALUE subScreenUpdate(VALUE self); ///< Update screen VALUE subScreenJump(VALUE self); ///< Jump to this screen VALUE subScreenViewReader(VALUE self); ///< Get screen view VALUE subScreenViewWriter(VALUE self, VALUE value); ///< Set screen view VALUE subScreenAskCurrent(VALUE self); ///< Whether screen is current VALUE subScreenToString(VALUE self); ///< Screen to string /* }}} */ /* sublet.c {{{ */ /* Singleton */ VALUE subSubletSingFind(VALUE self, VALUE value); ///< Find sublet VALUE subSubletSingFirst(VALUE self, VALUE value); ///< Find first sublet VALUE subSubletSingList(VALUE self); ///< Get all sublets /* Class */ VALUE subSubletInit(VALUE self, VALUE name); ///< Create sublet VALUE subSubletUpdate(VALUE self); ///< Update sublet VALUE subSubletSend(VALUE self, VALUE value); ///< Send data to sublet VALUE subSubletVisibilityShow(VALUE self); ///< Show sublet VALUE subSubletVisibilityHide(VALUE self); ///< Hide sublet VALUE subSubletGeometryReader(VALUE self); ///< Get sublet geometry VALUE subSubletToString(VALUE self); ///< Sublet to string VALUE subSubletKill(VALUE self); ///< Kill sublet /* }}} */ /* subtle.c {{{ */ /* Singleton */ VALUE subSubtleSingDisplayReader(VALUE self); ///< Get display VALUE subSubtleSingDisplayWriter(VALUE self, VALUE display); ///< Set display VALUE subSubtleSingAskRunning(VALUE self); ///< Is subtle running VALUE subSubtleSingSelect(VALUE self); ///< Select window VALUE subSubtleSingRender(VALUE self); ///< Render panels VALUE subSubtleSingReload(VALUE self); ///< Reload config and sublets VALUE subSubtleSingRestart(VALUE self); ///< Restart subtle VALUE subSubtleSingQuit(VALUE self); ///< Quit subtle VALUE subSubtleSingColors(VALUE self); ///< Get colors VALUE subSubtleSingFont(VALUE self); ///< Get font VALUE subSubtleSingSpawn(VALUE self, VALUE cmd); ///< Spawn command /* }}} */ /* subtlext.c {{{ */ void subSubtlextConnect(char *display_string); ///< Connect to display void subSubtlextBacktrace(void); ///< Print ruby backtrace VALUE subSubtlextConcat(VALUE str1, VALUE str2); ///< Concat strings VALUE subSubtlextParse(VALUE value, char *buf, int len, int *flags); ///< Parse arguments VALUE subSubtlextOneOrMany(VALUE value, VALUE prev); ///< Return one or many VALUE subSubtlextManyToOne(VALUE value); ///< Return one from many Window *subSubtlextWindowList(char *prop_name, int *size); ///< Get window list int subSubtlextFindString(char *prop_name, char *source, char **name, int flags); ///< Find string id VALUE subSubtlextFindObjects(char *prop_name, char *class_name, char *source, int flags, int first); ///< Find objects VALUE subSubtlextFindWindows(char *prop_name, char *class_name, char *source, int flags, int first); ///< Find objects VALUE subSubtlextFindObjectsGeometry(char *prop_name, char *class_name, char *source, int flags, int first); ///< Find objects with geometries /* }}} */ /* tag.c {{{ */ /* Singleton */ VALUE subTagSingFind(VALUE self, VALUE value); ///< Find tag VALUE subTagSingFirst(VALUE self, VALUE value); ///< Find first tag VALUE subTagSingVisible(VALUE self); ///< Get all visible tags VALUE subTagSingList(VALUE self); ///< Get all tags /* Class */ VALUE subTagInstantiate(char *name); ///< Instantiate tag VALUE subTagInit(VALUE self, VALUE name); ///< Create tag VALUE subTagSave(VALUE self); ///< Save tag VALUE subTagClients(VALUE self); ///< Get clients with tag VALUE subTagViews(VALUE self); ///< Get views with tag VALUE subTagToString(VALUE self); ///< Tag to string VALUE subTagKill(VALUE self); ///< Kill tag /* }}} */ /* tray.c {{{ */ /* Singleton */ VALUE subTraySingFind(VALUE self, VALUE name); ///< Find tray VALUE subTraySingFirst(VALUE self, VALUE name); ///< Find first tray VALUE subTraySingList(VALUE self); ///< Get all trays /* Class */ VALUE subTrayInstantiate(Window win); ///< Instantiate tray VALUE subTrayInit(VALUE self, VALUE win); ///< Create tray VALUE subTrayUpdate(VALUE self); ///< Update tray VALUE subTrayToString(VALUE self); ///< Tray to string VALUE subTrayKill(VALUE self); ///< Kill tray /* }}} */ /* view.c {{{ */ /* Singleton */ VALUE subViewSingFind(VALUE self, VALUE name); ///< Find view VALUE subViewSingFirst(VALUE self, VALUE name); ///< Find first view VALUE subViewSingCurrent(VALUE self); ///< Get current view VALUE subViewSingVisible(VALUE self); ///< Get all visible views VALUE subViewSingList(VALUE self); ///< Get all views /* Class */ VALUE subViewInstantiate(char *name); ///< Instantiate view VALUE subViewInit(VALUE self, VALUE name); ///< Create view VALUE subViewUpdate(VALUE self); ///< Update view VALUE subViewSave(VALUE self); ///< Save view VALUE subViewClients(VALUE self); ///< Get clients of view VALUE subViewJump(VALUE self); ///< Jump to view VALUE subViewSelectNext(VALUE self); ///< Select next view VALUE subViewSelectPrev(VALUE self); ///< Select next view VALUE subViewAskCurrent(VALUE self); ///< Whether view the current VALUE subViewIcon(VALUE self); ///< View icon if any VALUE subViewToString(VALUE self); ///< View to string VALUE subViewKill(VALUE self); ///< Kill view /* }}} */ /* window.c {{{ */ /* Singleton */ VALUE subWindowSingOnce(VALUE self, VALUE geometry); ///< Run window once /* Class */ VALUE subWindowInstantiate(VALUE geometry); ///< Instantiate window VALUE subWindowDispatcher(int argc, VALUE *argv, VALUE self); ///< Window dispatcher VALUE subWindowAlloc(VALUE self); ///< Allocate window VALUE subWindowInit(VALUE self, VALUE geometry); ///< Init window VALUE subWindowSubwindow(VALUE self, VALUE geometry); ///< Create a subwindow VALUE subWindowNameWriter(VALUE self, VALUE value); ///< Set name VALUE subWindowFontWriter(VALUE self, VALUE value); ///< Set font VALUE subWindowFontYReader(VALUE self); ///< Get y offset of font VALUE subWindowFontHeightReader(VALUE self); ///< Get height of font VALUE subWindowFontWidth(VALUE self, VALUE string); ///< Get string width for font VALUE subWindowForegroundWriter(VALUE self, VALUE value); ///< Set foreground VALUE subWindowBackgroundWriter(VALUE self, VALUE value); ///< Set background VALUE subWindowBorderColorWriter(VALUE self, VALUE value); ///< Set border color VALUE subWindowBorderSizeWriter(VALUE self, VALUE value); ///< Set border size VALUE subWindowGeometryReader(VALUE self); ///< Get geometry VALUE subWindowGeometryWriter(VALUE self, VALUE value); ///< Set geometry VALUE subWindowOn(int argc, VALUE *argv, VALUE self); ///< Add event handler VALUE subWindowDrawPoint(int argc, VALUE *argv, VALUE self); ///< Draw a point VALUE subWindowDrawLine(int argc, VALUE *argv, VALUE self); ///< Draw a line VALUE subWindowDrawRect(int argc, VALUE *argv, VALUE self); ///< Draw a rect VALUE subWindowDrawText(int arcg, VALUE *argv, VALUE self); ///< Draw text VALUE subWindowDrawIcon(int arcg, VALUE *argv, VALUE self); ///< Draw icon VALUE subWindowClear(int argc, VALUE *argv, VALUE self); ///< Clear area or window VALUE subWindowRedraw(VALUE self); ///< Redraw window VALUE subWindowRaise(VALUE self); ///< Raise window VALUE subWindowLower(VALUE self); ///< Lower window VALUE subWindowShow(VALUE self); ///< Show window VALUE subWindowHide(VALUE self); ///< Hide window VALUE subWindowAskHidden(VALUE self); ///< Whether window is hidden VALUE subWindowKill(VALUE self); ///< Kill window /* }}} */ #endif /* SUBTLEXT_H */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/subtle.c0000644000175000017500000002130011770332063020311 0ustar formorerformorer /** * @package subtle * * @file subtle ruby extension * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/subtle.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* SubtleSend {{{ */ static VALUE SubtleSend(char *message) { SubMessageData data = { { 0, 0, 0, 0, 0 } }; subSubtlextConnect(NULL); ///< Implicit open connection subSharedMessage(display, DefaultRootWindow(display), message, data, 32, True); return Qnil; } /* }}} */ /* Singleton */ /* subSubtleSingDisplayReader {{{ */ /* * call-seq: display -> String * * Get the display name. * * subtle.display * => ":0" */ VALUE subSubtleSingDisplayReader(VALUE self) { subSubtlextConnect(NULL); ///< Implicit open connection return rb_str_new2(DisplayString(display)); } /* }}} */ /* subSubtleSingDisplayWriter {{{ */ /* * call-seq: display=(string) -> nil * * Set the display name. * * subtle.display = ":0" * => nil */ VALUE subSubtleSingDisplayWriter(VALUE self, VALUE display_string) { /* Explicit open connection */ subSubtlextConnect(T_STRING == rb_type(display_string) ? RSTRING_PTR(display_string) : NULL); return Qnil; } /* }}} */ /* subSubtleSingAskRunning {{{ */ /* * call-seq: running? -> true or false * * Whether a subtle instance on current display is running. * * subtle.running? * => true * * subtle.running? * => false */ VALUE subSubtleSingAskRunning(VALUE self) { char *version = NULL; Window *support = NULL; VALUE running = Qfalse; subSubtlextConnect(NULL); ///< Implicit open connection /* Get supporting window */ if((support = (Window *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_WINDOW, XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False), NULL))) { /* Get version property */ if((version = subSharedPropertyGet(display, *support, XInternAtom(display, "UTF8_STRING", False), XInternAtom(display, "SUBTLE_VERSION", False), NULL))) { running = Qtrue; free(version); } free(support); } return running; } /* }}} */ /* subSubtleSingSelect {{{ */ /* * call-seq: select_window -> Fixnum * * Select a window and get the window id of it. * * select_window * => 8388617 */ VALUE subSubtleSingSelect(VALUE self) { int i, format = 0, buttons = 0; unsigned int nwins = 0; unsigned long nitems = 0, bytes = 0; unsigned char *data = NULL; XEvent event; Window win = None; Atom type = None, rtype = None; Window wroot = None, parent = None, root = None, *wins = NULL; Cursor cursor = None; subSubtlextConnect(NULL); ///< Implicit open connection root = DefaultRootWindow(display); cursor = XCreateFontCursor(display, XC_cross); type = XInternAtom(display, "WM_STATE", True); /* Grab pointer */ if(XGrabPointer(display, root, False, ButtonPressMask|ButtonReleaseMask, GrabModeSync, GrabModeAsync, root, cursor, CurrentTime)) { XFreeCursor(display, cursor); return Qnil; } /* Select a window */ while(None == win || 0 != buttons) { XAllowEvents(display, SyncPointer, CurrentTime); XWindowEvent(display, root, ButtonPressMask|ButtonReleaseMask, &event); switch(event.type) { case ButtonPress: if(None == win) win = event.xbutton.subwindow ? event.xbutton.subwindow : root; ///< Sanitize buttons++; break; case ButtonRelease: if(0 < buttons) buttons--; break; } } /* Find children with WM_STATE atom */ XQueryTree(display, win, &wroot, &parent, &wins, &nwins); for(i = 0; i < nwins; i++) { if(Success == XGetWindowProperty(display, wins[i], type, 0, 0, False, AnyPropertyType, &rtype, &format, &nitems, &bytes, &data)) { if(data) { XFree(data); data = NULL; } if(type == rtype) { win = wins[i]; break; } } } if(wins) XFree(wins); XFreeCursor(display, cursor); XUngrabPointer(display, CurrentTime); XSync(display, False); ///< Sync all changes return None != win ? LONG2NUM(win) : Qnil; } /* }}} */ /* subSubtleSingRender {{{ */ /* * call-seq: render -> nil * * Force Subtle to render screen panels. * * subtle.reload * => nil */ VALUE subSubtleSingRender(VALUE self) { return SubtleSend("SUBTLE_RENDER"); } /* }}} */ /* subSubtleSingReload {{{ */ /* * call-seq: reload -> nil * * Force Subtle to reload config and sublets. * * subtle.reload * => nil */ VALUE subSubtleSingReload(VALUE self) { return SubtleSend("SUBTLE_RELOAD"); } /* }}} */ /* subSubtleSingRestart {{{ */ /* * call-seq: restart -> nil * * Force Subtle to restart. * * subtle.restart * => nil */ VALUE subSubtleSingRestart(VALUE self) { return SubtleSend("SUBTLE_RESTART"); } /* }}} */ /* subSubtleSingQuit {{{ */ /* * call-seq: quit -> nil * * Force Subtle to exit. * * subtle.reload * => nil */ VALUE subSubtleSingQuit(VALUE self) { return SubtleSend("SUBTLE_QUIT"); } /* }}} */ /* subSubtleSingColors {{{ */ /* * call-seq: colors -> Hash * * Get an array of all Colors. * * Subtlext::Subtle.colors * => { :fg_panel => # } */ VALUE subSubtleSingColors(VALUE self) { int i; unsigned long ncolors = 0, *colors = NULL; VALUE meth = Qnil, klass = Qnil, hash = Qnil; const char *names[] = { "title_fg", "title_bg", "title_bo_top", "title_bo_right", "title_bo_bottom", "title_bo_left", "views_fg", "views_bg", "views_bo_top", "views_bo_right", "views_bo_bottom", "views_bo_left", "focus_fg", "focus_bg", "focus_bo_top", "focus_bo_right", "focus_bo_bottom", "focus_bo_left", "urgent_fg", "urgent_bg", "urgent_bo_top", "urgent_bo_right", "urgent_bo_bottom", "urgent_bo_left", "occupied_fg", "occupied_bg", "occupied_bo_top", "occupied_bo_right", "occupied_bo_bottom", "occupied_bo_left", "sublets_fg", "sublets_bg", "sublets_bo_top", "sublets_bo_right", "sublets_bo_bottom", "sublets_bo_left", "separator_fg", "separator_bg", "separator_bo_top", "separator_bo_right", "separator_bo_bottom", "separator_bo_left", "client_active", "client_inactive", "panel_top", "panel_bottom", "stipple", "background" }; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); klass = rb_const_get(mod, rb_intern("Color")); hash = rb_hash_new(); /* Check result */ if((colors = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_COLORS", False), &ncolors))) { for(i = 0; i < ncolors && i < LENGTH(names); i++) { VALUE c = rb_funcall(klass, meth, 1, LONG2NUM(colors[i])); rb_hash_aset(hash, CHAR2SYM(names[i]), c); } free(colors); } return hash; } /* }}} */ /* subSubtleSingFont {{{ */ /* * call-seq: Font -> String or nil * * Get the font used in subtle. * * Subtlext::Subtle.font * => "-*-*-medium-*-*-*-14-*-*-*-*-*-*-*" */ VALUE subSubtleSingFont(VALUE self) { char *prop = NULL; VALUE font = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Get results */ if((prop = subSharedPropertyGet(display, DefaultRootWindow(display), XInternAtom(display, "UTF8_STRING", False), XInternAtom(display, "SUBTLE_FONT", False), NULL))) { font = rb_str_new2(prop); free(prop); } return font; } /* }}} */ /* subSubtleSingSpawn {{{ */ /* * call-seq: spawn(cmd) -> Subtlext::Client * * Spawn a command and returns a Client object. * * spawn("xterm") * => # */ VALUE subSubtleSingSpawn(VALUE self, VALUE cmd) { VALUE ret = Qnil; /* Check object type */ if(T_STRING == rb_type(cmd)) { pid_t pid = 0; subSubtlextConnect(NULL); ///< Implicit open connection /* Create client with empty window id since we cannot * know the real window id at this point (race) */ if(0 < (pid = subSharedSpawn(RSTRING_PTR(cmd)))) { ret = subClientInstantiate((int)pid); rb_iv_set(ret, "@pid", INT2FIX((int)pid)); } } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(cmd)); return ret; } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/window.c0000644000175000017500000007654511770332063020347 0ustar formorerformorer /** * @package subtle * * @file subtle ruby extension * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/window.c,v 3204 2012/05/22 21:15:06 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* Flags {{{ */ #define WINDOW_INPUT_FUNC (1L << 2) #define WINDOW_FOREIGN_WIN (1L << 3) /* }}} */ /* Typedefs {{{ */ typedef struct subtlextwindow_t { GC gc; int flags, ntext; unsigned long fg, bg; Window win; VALUE instance, expose, keyboard, pointer; SubFont *font; } SubtlextWindow; /* }}} */ /* WindowMark {{{ */ static void WindowMark(SubtlextWindow *w) { if(w) { rb_gc_mark(w->instance); if(RTEST(w->expose)) rb_gc_mark(w->expose); if(RTEST(w->keyboard)) rb_gc_mark(w->keyboard); if(RTEST(w->pointer)) rb_gc_mark(w->pointer); } } /* }}} */ /* WindowSweep {{{ */ static void WindowSweep(SubtlextWindow *w) { if(w) { /* Destroy window */ if(!(w->flags & WINDOW_FOREIGN_WIN)) XDestroyWindow(display, w->win); if(0 != w->gc) XFreeGC(display, w->gc); if(w->font) subSharedFontKill(display, w->font); free(w); } } /* }}} */ /* WindowCall {{{ */ static VALUE WindowCall(VALUE data) { VALUE *rargs = (VALUE *)data; return rb_funcall(rargs[0], rargs[1], rargs[2], rargs[3], rargs[4]); } /* }}} */ /* WindowExpose {{{ */ static void WindowExpose(SubtlextWindow *w) { if(w) { XClearWindow(display, w->win); /* Call expose proc if any */ if(RTEST(w->expose)) { int state = 0; VALUE rargs[5] = { Qnil }; /* Wrap up data */ rargs[0] = w->expose; rargs[1] = rb_intern("call"); rargs[2] = 1; rargs[3] = w->instance; /* Carefully call listen proc */ rb_protect(WindowCall, (VALUE)&rargs, &state); if(state) subSubtlextBacktrace(); } XSync(display, False); ///< Sync with X } } /* }}} */ /* WindowGrab {{{ */ static VALUE WindowGrab(SubtlextWindow *w) { XEvent ev; int loop = True, state = 0; char buf[32] = { 0 }; unsigned long *focus = NULL, mask = 0; VALUE result = Qnil, rargs[5] = { Qnil }, sym = Qnil, ary = Qnil; KeySym keysym; /* Add grabs */ if(RTEST(w->keyboard)) { mask |= KeyPressMask; XGrabKeyboard(display, w->win, True, GrabModeAsync, GrabModeAsync, CurrentTime); } if(RTEST(w->pointer)) { mask |= ButtonPressMask; XGrabPointer(display, w->win, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); } XMapRaised(display, w->win); XSelectInput(display, w->win, mask); XSetInputFocus(display, w->win, RevertToPointerRoot, CurrentTime); WindowExpose(w); XFlush(display); while(loop) { XMaskEvent(display, mask, &ev); switch(ev.type) { case KeyPress: /* {{{ */ XLookupString(&ev.xkey, buf, sizeof(buf), &keysym, NULL); /* Skip modifier keys */ if(IsModifierKey(keysym)) continue; /* Translate syms to something meaningful */ switch(keysym) { /* Arrow keys */ case XK_KP_Left: case XK_Left: sym = CHAR2SYM("left"); break; case XK_KP_Right: case XK_Right: sym = CHAR2SYM("right"); break; case XK_KP_Up: case XK_Up: sym = CHAR2SYM("up"); break; case XK_KP_Down: case XK_Down: sym = CHAR2SYM("down"); break; /* Input */ case XK_KP_Enter: case XK_Return: sym = CHAR2SYM("return"); break; case XK_Escape: sym = CHAR2SYM("escape"); break; case XK_BackSpace: sym = CHAR2SYM("backspace"); break; case XK_Tab: sym = CHAR2SYM("tab"); break; case XK_space: sym = CHAR2SYM("space"); break; default: sym = CHAR2SYM(buf); } /* Translate modifier keys */ if(0 != ev.xkey.state) { ary = rb_ary_new(); if(ev.xkey.state & ShiftMask) rb_ary_push(ary, CHAR2SYM("shift")); if(ev.xkey.state & ControlMask) rb_ary_push(ary, CHAR2SYM("control")); if(ev.xkey.state & Mod1Mask) rb_ary_push(ary, CHAR2SYM("alt")); if(ev.xkey.state & Mod2Mask) rb_ary_push(ary, CHAR2SYM("meta")); if(ev.xkey.state & Mod3Mask) rb_ary_push(ary, CHAR2SYM("super")); if(ev.xkey.state & Mod4Mask) rb_ary_push(ary, CHAR2SYM("altgr")); } /* Wrap up data */ rargs[0] = w->keyboard; rargs[1] = rb_intern("call"); rargs[2] = 2; rargs[3] = sym; rargs[4] = ary; /* Carefully call listen proc */ result = rb_protect(WindowCall, (VALUE)&rargs, &state); if(state) subSubtlextBacktrace(); /* End event loop? */ if(Qtrue != result || state) loop = False; break; /* }}} */ case ButtonPress: /* {{{ */ /* Wrap up data */ rargs[0] = w->pointer; rargs[1] = rb_intern("call"); rargs[2] = 2; rargs[3] = INT2FIX(ev.xbutton.x); rargs[4] = INT2FIX(ev.xbutton.y); /* Carefully call listen proc */ result = rb_protect(WindowCall, (VALUE)&rargs, &state); if(state) subSubtlextBacktrace(); /* End event loop? */ if(Qtrue != result || state) loop = False; break; /* }}} */ default: break; } } /* Remove grabs */ if(RTEST(w->keyboard)) { XSelectInput(display, w->win, NoEventMask); XUngrabKeyboard(display, CurrentTime); } if(RTEST(w->pointer)) XUngrabPointer(display, CurrentTime); /* Restore logical focus */ if((focus = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_WINDOW, XInternAtom(display, "_NET_ACTIVE_WINDOW", False), NULL))) { XSetInputFocus(display, *focus, RevertToPointerRoot, CurrentTime); free(focus); } return Qnil; } /* }}} */ /* Singleton */ /* subWindowSingOnce {{{ */ /* * call-seq: once(geometry) -> Value * * Show window once as long as proc runs * * Subtlext::Window.once(:x => 10, :y => 10, :widht => 100, :height => 100) do |w| * "test" * end * => "test" **/ VALUE subWindowSingOnce(VALUE self, VALUE geometry) { VALUE win = Qnil, ret = Qnil; rb_need_block(); /* Create new window */ win = subWindowInstantiate(geometry); /* Yield block */ ret = rb_yield_values(1, win); subWindowKill(win); return ret; } /* }}} */ /* Helper */ /* subWindowInstantiate {{{ */ VALUE subWindowInstantiate(VALUE geometry) { VALUE klass = Qnil, win = Qnil; /* Create new instance */ klass = rb_const_get(mod, rb_intern("Window")); win = rb_funcall(klass, rb_intern("new"), 1, geometry); return win; } /* }}} */ /* Class */ /* subWindowAlloc {{{ */ /* * call-seq: new(geometry) -> Subtlext::Window * * Allocate space for new Window object. **/ VALUE subWindowAlloc(VALUE self) { SubtlextWindow *w = NULL; /* Create window */ w = (SubtlextWindow *)subSharedMemoryAlloc(1, sizeof(SubtlextWindow)); w->instance = Data_Wrap_Struct(self, WindowMark, WindowSweep, (void *)w); return w->instance; } /* }}} */ /* subWindowInit {{{ * * call-seq: new(geometry, &block) -> Subtlext::Window * * Initialize Window object. * * win = Subtlext::Window.new(:x => 5, :y => 5) do |w| * s.background = "#ffffff" * end */ VALUE subWindowInit(VALUE self, VALUE value) { SubtlextWindow *w = NULL; Data_Get_Struct(self, SubtlextWindow, w); if(w) { VALUE geometry = Qnil; subSubtlextConnect(NULL); ///< Implicit open connection /* Check object type */ switch(rb_type(value)) { case T_HASH: case T_ARRAY: { XSetWindowAttributes sattrs; XRectangle r = { 0 }; /* Create geometry */ geometry = subGeometryInstantiate(0, 0, 1, 1); geometry = subGeometryInit(1, &value, geometry); subGeometryToRect(geometry, &r); /* Create window */ sattrs.override_redirect = True; w->win = XCreateWindow(display, DefaultRootWindow(display), r.x, r.y, r.width, r.height, 1, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &sattrs); } break; case T_FIXNUM: case T_BIGNUM: { int x = 0, y = 0; unsigned int width = 0, height = 0, bw = 0, depth = 0; Window root = None; /* Update values */ w->win = FIX2LONG(value); w->flags |= WINDOW_FOREIGN_WIN; /* Get window geometry */ if(XGetGeometry(display, w->win, &root, &x, &y, &width, &height, &bw, &depth)) geometry = subGeometryInstantiate(x, y, width, height); else rb_raise(rb_eArgError, "Invalid window `%#lx'", w->win); } break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } /* Store data */ rb_iv_set(w->instance, "@win", LONG2NUM(w->win)); rb_iv_set(w->instance, "@geometry", geometry); rb_iv_set(w->instance, "@hidden", Qtrue); /* Set window font */ if(!w->font && !(w->font = subSharedFontNew(display, DEFFONT))) rb_raise(rb_eStandardError, "Invalid font `%s'", DEFFONT); /* Yield to block if given */ if(rb_block_given_p()) rb_yield_values(1, w->instance); XSync(display, False); ///< Sync with X } return Qnil; } /* }}} */ /* subWindowSubwindow {{{ * * call-seq: subwindow(geometry, &block) -> Subtlext::Window or nil * * Create a subwindow of Window with given Geometry. * * win.subwindow(:x => 5, :y => 5) do |w| * s.background = "#ffffff" * end */ VALUE subWindowSubwindow(VALUE self, VALUE geometry) { VALUE ret = Qnil; SubtlextWindow *w1 = NULL; Data_Get_Struct(self, SubtlextWindow, w1); if(w1) { SubtlextWindow *w2 = NULL; subSubtlextConnect(NULL); ///< Implicit open connection ret = subWindowInstantiate(geometry); Data_Get_Struct(ret, SubtlextWindow, w2); if(w2) { /* Yield to block if given */ if(rb_block_given_p()) rb_yield_values(1, w2->instance); XReparentWindow(display, w2->win, w1->win, 0, 0); } } return ret; } /* }}} */ /* subWindowNameWriter {{{ */ /* * call-seq: name=(str) -> String * * Set the WM_NAME of a Window- * * win.name = "sublet" * => "sublet" */ VALUE subWindowNameWriter(VALUE self, VALUE value) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { Window win = None; XClassHint hint; XTextProperty text; char *name = NULL; /* Check object type */ if(T_STRING == rb_type(value)) { name = RSTRING_PTR(value); win = NUM2LONG(rb_iv_get(self, "@win")); /* Set Window informations */ hint.res_name = name; hint.res_class = "Subtlext"; XSetClassHint(display, win, &hint); XStringListToTextProperty(&name, 1, &text); XSetWMName(display, win, &text); free(text.value); } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } return value; } /* }}} */ /* subWindowFontWriter {{{ */ /* * call-seq: font=(string) -> String * * Set the font that is used for text inside of a W.indow * * win.font = "-*-*-*-*-*-*-10-*-*-*-*-*-*-*" * => "-*-*-*-*-*-*-10-*-*-*-*-*-*-*" */ VALUE subWindowFontWriter(VALUE self, VALUE value) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { /* Check object type */ if(T_STRING == rb_type(value)) { SubFont *f = NULL; char *font = RSTRING_PTR(value); /* Create window font */ if((f = subSharedFontNew(display, font))) { /* Replace font */ if(w->font) subSharedFontKill(display, w->font); w->font = f; } else rb_raise(rb_eStandardError, "Invalid font `%s'", font); } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } return value; } /* }}} */ /* subWindowFontYReader {{{ */ /* * call-seq: font_y -> Fixnum * * Get y offset of the selected Window font. * * win.font_y * => 10 */ VALUE subWindowFontYReader(VALUE self) { VALUE ret = INT2FIX(0); SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w && w->font) ret = INT2FIX(w->font->y); return ret; } /* }}} */ /* subWindowFontHeightReader {{{ */ /* * call-seq: font_height -> Fixnum * * Get the height of selected Window font. * * win.font_height * => 10 */ VALUE subWindowFontHeightReader(VALUE self) { VALUE ret = INT2FIX(0); SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w && w->font) ret = INT2FIX(w->font->height); return ret; } /* }}} */ /* subWindowFontWidth {{{ */ /* * call-seq: font_width(string) -> Fixnum * * Get width of string for selected Window font. * * win.font_width("subtle") * => 10 */ VALUE subWindowFontWidth(VALUE self, VALUE string) { VALUE ret = INT2FIX(0); SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w && w->font && T_STRING == rb_type(string)) ret = INT2FIX(subSharedStringWidth(display, w->font, RSTRING_PTR(string), RSTRING_LEN(string), NULL, NULL, False)); return ret; } /* }}} */ /* subWindowForegroundWriter {{{ */ /* * call-seq: foreground=(string) -> String * foreground=(array) -> Array * foreground=(hash) -> Hash * foreground=(fixnum) -> Fixnum * foreground=(object) -> Subtlext::Color * * Set the foreground color of this Window which can be of * following types: * * [String] Any color representation of Xlib is allowed * [Array] Must be an array with values for red, green and blue * [Hash] Must be a hash with values for red, green and blue * [Fixnum] Pixel representation of a color in Xlib * [Object] Copy color from a Color object * * win.foreground = "#000000" * => "#000000" */ VALUE subWindowForegroundWriter(VALUE self, VALUE value) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) w->fg = subColorPixel(value, Qnil, Qnil, NULL); return value; } /* }}} */ /* subWindowBackgroundWriter {{{ */ /* * call-seq: background=(string) -> String * background=(array) -> Array * background=(hash) -> Hash * background=(fixnum) -> Fixnum * background=(object) -> Subtlext::Color * * Set the background color of this Window which can be of * following types: * * [String] Any color representation of Xlib is allowed * [Array] Must be an array with values for red, green and blue * [Hash] Must be a hash with values for red, green and blue * [Fixnum] Pixel representation of a color in Xlib * [Object] Copy color from a Color object * win.background = "#000000" * => "#000000" */ VALUE subWindowBackgroundWriter(VALUE self, VALUE value) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { w->bg = subColorPixel(value, Qnil, Qnil, NULL); XSetWindowBackground(display, w->win, w->bg); } return value; } /* }}} */ /* subWindowBorderColorWriter {{{ */ /* * call-seq: border=(string) -> String * border=(array) -> Array * border=(hash) -> Hash * border=(fixnum) -> Fixnum * border=(object) -> Subtlext::Color * * Set the border color of this Window which can be of * following types: * * [String] Any color representation of Xlib is allowed * [Array] Must be an array with values for red, green and blue * [Hash] Must be a hash with values for red, green and blue * [Fixnum] Pixel representation of a color in Xlib * [Object] Copy color from a Color object * * win.border_color = "#000000" * => "#000000" */ VALUE subWindowBorderColorWriter(VALUE self, VALUE value) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { XSetWindowBorder(display, w->win, subColorPixel(value, Qnil, Qnil, NULL)); XFlush(display); } return Qnil; } /* }}} */ /* subWindowBorderSizeWriter {{{ */ /* * call-seq: border_size=(fixnum) -> Fixnum * * Set border size of this Window. * * win.border_size = 3 * => 3 */ VALUE subWindowBorderSizeWriter(VALUE self, VALUE value) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { int width = 3; /* Check object type */ if(FIXNUM_P(value)) { width = FIX2INT(value); XSetWindowBorderWidth(display, w->win, width); XFlush(display); } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } return value; } /* }}} */ /* subWindowGeometryReader {{{ */ /* * call-seq: geometry -> Subtlext::Geometry * * Get the Geometry of this Window. * * win.geometry * => # */ VALUE subWindowGeometryReader(VALUE self) { VALUE geom = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@geometry", geom); return geom; } /* }}} */ /* subWindowGeometryWriter {{{ */ /* * call-seq: geometry=(array) -> Array * geometry=(hash) -> Hash * geometry=(object) -> Subtlext::Geometry * * Set the geometry of this Window which can be of following * types: * * [Array] Must be an array with values for x, y, width and height * [Hash] Must be a hash with values for x, y, width and height * [Geometry] Copy geometry from a Geometry object * * win.geometry = { :x => 0, :y => 0, :width => 50, :height => 50 } * => { :x => 0, :y => 0, :width => 50, :height => 50 } */ VALUE subWindowGeometryWriter(VALUE self, VALUE value) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { XRectangle r = { 0 }; VALUE geom = Qnil; /* Create geometry */ geom = subGeometryInstantiate(0, 0, 1, 1); geom = subGeometryInit(1, &value, geom); rb_iv_set(self, "@geometry", geom); subGeometryToRect(geom, &r); XMoveResizeWindow(display, w->win, r.x, r.y, r.width, r.height); } return value; } /* }}} */ /* subWindowOn {{{ */ /* * call-seq: on(event, &block) -> Subtlext::Window * * Grab pointer button press events and pass them to the block until * the return value of the block isn't true or an error occured. * * grab_mouse do |x, y, button| * p "x=#{x}, y=#{y}, button=#{button}" * end * => # */ VALUE subWindowOn(int argc, VALUE *argv, VALUE self) { VALUE event = Qnil, value = Qnil; SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); rb_scan_args(argc, argv, "11", &event, &value); if(rb_block_given_p()) value = rb_block_proc(); ///< Get proc Data_Get_Struct(self, SubtlextWindow, w); if(w) { /* Check value type */ if(CHAR2SYM("draw") == event || CHAR2SYM("redraw") == event || CHAR2SYM("expose") == event) { w->expose = value; } else if(CHAR2SYM("key_down") == event) { w->keyboard = value; } else if(CHAR2SYM("mouse_down") == event) { w->pointer = value; } else rb_raise(rb_eArgError, "Unexpected value type for on"); } return self; } /* }}} */ /* subWindowDrawPoint {{{ */ /* * call-seq: draw_point(x, y, color) -> Subtlext::Window * * Draw a pixel on the window at given coordinates in given color. * * win.draw_point(1, 1) * => # * * win.draw_point(1, 1, "#ff0000") * => # */ VALUE subWindowDrawPoint(int argc, VALUE *argv, VALUE self) { VALUE x = Qnil, y = Qnil, color = Qnil; rb_scan_args(argc, argv, "21", &x, &y, &color); /* Check object types */ if(FIXNUM_P(x) && FIXNUM_P(y)) { SubtlextWindow *w = NULL; Data_Get_Struct(self, SubtlextWindow, w); if(w) { XGCValues gvals; /* Create on demand */ if(0 == w->gc) w->gc = XCreateGC(display, w->win, 0, NULL); /* Update GC */ gvals.foreground = w->fg; gvals.background = w->bg; if(!NIL_P(color)) gvals.foreground = subColorPixel(color, Qnil, Qnil, NULL); XChangeGC(display, w->gc, GCForeground|GCBackground, &gvals); XDrawPoint(display, w->win, w->gc, FIX2INT(x), FIX2INT(y)); XFlush(display); } } else rb_raise(rb_eArgError, "Unexpected value-types"); return self; } /* }}} */ /* subWindowDrawLine {{{ */ /* * call-seq: draw_line(x1, y1, x2, y2, color) -> Subtlext::Window * * Draw a line on the window starting at x1/y1 to x2/y2 in given color. * * win.draw_line(1, 1, 10, 1) * => # * * win.draw_line(1, 1, 10, 1, "#ff0000", "#000000") * => # */ VALUE subWindowDrawLine(int argc, VALUE *argv, VALUE self) { VALUE x1 = Qnil, x2 = Qnil, y1 = Qnil, y2 = Qnil, color = Qnil; rb_scan_args(argc, argv, "41", &x1, &y1, &x2, &y2, &color); /* Check object types */ if(FIXNUM_P(x1) && FIXNUM_P(y1) && FIXNUM_P(x2) && FIXNUM_P(x2)) { SubtlextWindow *w = NULL; Data_Get_Struct(self, SubtlextWindow, w); if(w) { XGCValues gvals; /* Create on demand */ if(0 == w->gc) w->gc = XCreateGC(display, w->win, 0, NULL); /* Update GC */ gvals.foreground = w->fg; gvals.background = w->bg; if(!NIL_P(color)) gvals.foreground = subColorPixel(color, Qnil, Qnil, NULL); XChangeGC(display, w->gc, GCForeground|GCBackground, &gvals); XDrawLine(display, w->win, w->gc, FIX2INT(x1), FIX2INT(y1), FIX2INT(x2), FIX2INT(y2)); XFlush(display); } } else rb_raise(rb_eArgError, "Unexpected value-types"); return self; } /* }}} */ /* subWindowDrawRect {{{ */ /* * call-seq: draw_rect(x, y, width, height, color, fill) -> Subtlext::Window * * Draw a rect on the Window starting at x/y with given width, height * and colors. * * win.draw_rect(1, 1, 10, 10) * => # * * win.draw_rect(1, 1, 10, 10, "#ff0000", true) * => # */ VALUE subWindowDrawRect(int argc, VALUE *argv, VALUE self) { VALUE x = Qnil, y = Qnil, width = Qnil, height = Qnil; VALUE color = Qnil, fill = Qnil; rb_scan_args(argc, argv, "42", &x, &y, &width, &height, &color, &fill); /* Check object types */ if(FIXNUM_P(x) && FIXNUM_P(y) && FIXNUM_P(width) && FIXNUM_P(height)) { SubtlextWindow *w = NULL; Data_Get_Struct(self, SubtlextWindow, w); if(w) { XGCValues gvals; /* Create on demand */ if(0 == w->gc) w->gc = XCreateGC(display, w->win, 0, NULL); /* Update GC */ gvals.foreground = w->fg; gvals.background = w->bg; if(!NIL_P(color)) gvals.foreground = subColorPixel(color, Qnil, Qnil, NULL); XChangeGC(display, w->gc, GCForeground|GCBackground, &gvals); /* Draw rect */ if(Qtrue == fill) { XFillRectangle(display, w->win, w->gc, FIX2INT(x), FIX2INT(y), FIX2INT(width), FIX2INT(height)); } else XDrawRectangle(display, w->win, w->gc, FIX2INT(x), FIX2INT(y), FIX2INT(width), FIX2INT(height)); XFlush(display); } } else rb_raise(rb_eArgError, "Unexpected value-types"); return self; } /* }}} */ /* subWindowDrawText {{{ */ /* * call-seq: draw_text(x, y, string, color) -> Subtlext::Window * * Draw a text on the Window starting at x/y with given width, height * and color without caching it. * * win.draw_text(10, 10, "subtle") * => # */ VALUE subWindowDrawText(int argc, VALUE *argv, VALUE self) { SubtlextWindow *w = NULL; VALUE x = Qnil, y = Qnil, text = Qnil, color = Qnil; /* Check ruby object */ rb_check_frozen(self); rb_scan_args(argc, argv, "31", &x, &y, &text, &color); Data_Get_Struct(self, SubtlextWindow, w); if(w && FIXNUM_P(x) && FIXNUM_P(y) && T_STRING == rb_type(text)) { long lcolor = w->fg; /* Create on demand */ if(0 == w->gc) w->gc = XCreateGC(display, w->win, 0, NULL); /* Parse colors */ if(!NIL_P(color)) lcolor = subColorPixel(color, Qnil, Qnil, NULL); subSharedDrawString(display, w->gc, w->font, w->win, FIX2INT(x), FIX2INT(y), lcolor, w->bg, RSTRING_PTR(text), RSTRING_LEN(text)); } return self; } /* }}} */ /* subWindowDrawIcon {{{ */ /* * call-seq: draw_icon(x, y, icon, fg, bg) -> Subtlext::Window * * Draw a icon on the Window starting at x/y with given width, height * and color without caching it. * * win.draw_icon(10, 10, Subtlext::Icon.new("foo.xbm")) * => # */ VALUE subWindowDrawIcon(int argc, VALUE *argv, VALUE self) { SubtlextWindow *w = NULL; VALUE x = Qnil, y = Qnil, icon = Qnil, fg = Qnil, bg = Qnil; /* Check ruby object */ rb_check_frozen(self); rb_scan_args(argc, argv, "32", &x, &y, &icon, &fg, &bg); Data_Get_Struct(self, SubtlextWindow, w); if(w && FIXNUM_P(x) && FIXNUM_P(y) && rb_obj_is_instance_of(icon, rb_const_get(mod, rb_intern("Icon")))) { int bitmap = False; long lfg = w->fg, lbg = w->bg; VALUE width = Qnil, height = Qnil, pixmap = Qnil; /* Create on demand */ if(0 == w->gc) w->gc = XCreateGC(display, w->win, 0, NULL); /* Parse colors */ if(!NIL_P(fg)) lfg = subColorPixel(fg, Qnil, Qnil, NULL); if(!NIL_P(bg)) lbg = subColorPixel(bg, Qnil, Qnil, NULL); /* Fetch icon values */ width = rb_iv_get(icon, "@width"); height = rb_iv_get(icon, "@height"); pixmap = rb_iv_get(icon, "@pixmap"); bitmap = Qtrue == subIconAskBitmap(icon) ? True : False; subSharedDrawIcon(display, w->gc, w->win, FIX2INT(x), FIX2INT(y), FIX2INT(width), FIX2INT(height), lfg, lbg, NUM2LONG(pixmap), bitmap); } return self; } /* }}} */ /* subWindowClear {{{ */ /* * call-seq: clear -> Subtlext::Window * * Clear this Window and remove all stored text. * * win.clear * => # */ VALUE subWindowClear(int argc, VALUE *argv, VALUE self) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { VALUE x = Qnil, y = Qnil, width = Qnil, height = Qnil; rb_scan_args(argc, argv, "04", &x, &y, &width, &height); /* Either clear area or whole window */ if(FIXNUM_P(x) && FIXNUM_P(y) && FIXNUM_P(width) && FIXNUM_P(height)) { XClearArea(display, w->win, FIX2INT(x), FIX2INT(y), FIX2INT(width), FIX2INT(height), False); } else XClearWindow(display, w->win); } return self; } /* }}} */ /* subWindowRedraw {{{ */ /* * call-seq: redraw -> Subtlext::Window * * Redraw Window content. * * win.redraw * => # */ VALUE subWindowRedraw(VALUE self) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) WindowExpose(w); return self; } /* }}} */ /* subWindowRaise {{{ */ /* * call-seq: raise -> Subtlext::Window * * Raise this Window to the top of the window stack, when the window manager * supports that. (subtle does) * * win.raise * => # */ VALUE subWindowRaise(VALUE self) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { XRaiseWindow(display, w->win); WindowExpose(w); } return self; } /* }}} */ /* subWindowLower {{{ */ /* * call-seq: lower -> Subtlext::Window * * Lower this Window to the bottom of the window stack, when the window manager * supports that. (subtle does) * * win.lower * => # */ VALUE subWindowLower(VALUE self) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { XLowerWindow(display, w->win); WindowExpose(w); } return self; } /* }}} */ /* subWindowShow {{{ */ /* * call-seq: show() -> Subtlext::Window * * Show this Window on screen. * * win.show * => # */ VALUE subWindowShow(VALUE self) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { rb_iv_set(self, "@hidden", Qfalse); if(RTEST(w->keyboard) || RTEST(w->pointer)) WindowGrab(w); else { XMapRaised(display, w->win); WindowExpose(w); } } return self; } /* }}} */ /* subWindowHide {{{ */ /* * call-seq: hide() -> Subtlext::Window * * Hide this Window from screen. * * win.hide * => # */ VALUE subWindowHide(VALUE self) { VALUE win = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); if(RTEST(win)) { rb_iv_set(self, "@hidden", Qtrue); XUnmapWindow(display, NUM2LONG(win)); XSync(display, False); ///< Sync with X } return self; } /* }}} */ /* subWindowAskHidden {{{ */ /* * call-seq: hidden -> true or false * * Whether Window is hidden. * * win.hidden? * => true */ VALUE subWindowAskHidden(VALUE self) { VALUE hidden = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@hidden", hidden); return hidden; } /* }}} */ /* subWindowKill {{{ */ /* * call-seq: kill() -> nil * * Destroy this Window and freeze this object. * * win.kill * => nil */ VALUE subWindowKill(VALUE self) { SubtlextWindow *w = NULL; /* Check ruby object */ rb_check_frozen(self); Data_Get_Struct(self, SubtlextWindow, w); if(w) { XUnmapWindow(display, w->win); rb_obj_freeze(self); ///< Freeze object } return Qnil; } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/sublet.c0000644000175000017500000001622711770332063020325 0ustar formorerformorer /** * @package subtle * * @file subtle ruby extension * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/sublet.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* SubletFind {{{ */ static VALUE SubletFind(VALUE value, int first) { int flags = 0; VALUE parsed = Qnil; char buf[50] = { 0 }; subSubtlextConnect(NULL); ///< Implicit open connection /* Check object type */ switch(rb_type(parsed = subSubtlextParse( value, buf, sizeof(buf), &flags))) { case T_SYMBOL: if(CHAR2SYM("all") == parsed) return subSubletSingList(Qnil); break; case T_OBJECT: if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("Sublet")))) return parsed; } return subSubtlextFindObjectsGeometry("SUBTLE_SUBLET_LIST", "Sublet", buf, flags, first); } /* }}} */ /* Singleton */ /* subSubletSingFind {{{ */ /* * call-seq: find(value) -> Array * [value] -> Array * * Find Sublet by a given value which can be of following type: * * [Fixnum] Array index of the SUBTLE_SUBLET_LIST property list. * [String] Regexp match against name of Sublets, returns a Sublet on single * match or an Array on multiple matches. * [Symbol] Either :all for an array of all Sublets or any string for * an exact match. * * Subtlext::Sublet.find(1) * => [#] * * Subtlext::Sublet.find("subtle") * => [#] * * Subtlext::Sublet[".*"] * => [#, #] * * Subtlext::Sublet["subtle"] * => [] * * Subtlext::Sublet[:clock] * => [#] */ VALUE subSubletSingFind(VALUE self, VALUE value) { return SubletFind(value, False); } /* }}} */ /* subSubletSingFirst {{{ */ /* * call-seq: first(value) -> Subtlext::Sublet or nil * * Find first Sublet by a given value which can be of following type: * * [Fixnum] Array index of the SUBTLE_SUBLET_LIST property list. * [String] Regexp match against name of Sublets, returns a Sublet on single * match or an Array on multiple matches. * [Symbol] Either :all for an array of all Sublets or any string for * an exact match. * * Subtlext::Sublet.first(1) * => # * * Subtlext::Sublet.first("subtle") * => # */ VALUE subSubletSingFirst(VALUE self, VALUE value) { return SubletFind(value, True); } /* }}} */ /* subSubletSingList {{{ */ /* * call-seq: list -> Array * * Get an array of all Sublets based on the SUBTLE_SUBLET_LIST * property list. * * Subtlext::Sublet.list * => [#, #] * * Subtlext::Sublet.list * => [] */ VALUE subSubletSingList(VALUE self) { return subSubtlextFindObjectsGeometry("SUBTLE_SUBLET_LIST", "Sublet", NULL, 0, False); } /* }}} */ /* Class */ /* subSubletInit {{{ */ /* * call-seq: new(name) -> Subtlext::Sublet * * Create new Sublet object locally without calling #save automatically. * * sublet = Subtlext::Sublet.new("subtle") * => # */ VALUE subSubletInit(VALUE self, VALUE name) { if(T_STRING != rb_type(name)) rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(name)); /* Init object */ rb_iv_set(self, "@id", Qnil); rb_iv_set(self, "@name", name); subSubtlextConnect(NULL); ///< Implicit open connection return self; } /* }}} */ /* subSubletUpdate {{{ */ /* * call-seq: update -> Subtlext::Sublet * * Force subtle to update the data of this Sublet. * * sublet.update * => # */ VALUE subSubletUpdate(VALUE self) { VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); /* Send message */ data.l[0] = FIX2INT(id); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_SUBLET_UPDATE", data, 32, True); return self; } /* }}} */ /* subSubletSend {{{ */ /* * call-seq: send_data(string) -> Subtlext::Sublet * * Send given string data to a :data event of a Sublet. The data is * passed as second argument. * * sublet.send_data("subtle") * => # */ VALUE subSubletSend(VALUE self, VALUE value) { VALUE id = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); /* Check object type */ if(T_STRING == rb_type(value)) { char *list = NULL; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Store data */ list = strdup(RSTRING_PTR(value)); subSharedPropertySetStrings(display, DefaultRootWindow(display), XInternAtom(display, "SUBTLE_DATA", False), &list, 1); free(list); data.l[0] = FIX2INT(id); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_SUBLET_DATA", data, 32, True); } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); return self; } /* }}} */ /* subSubletVisibilityShow {{{ */ /* * call-seq: show -> Subtlext::Sublet * * Show sublet in the panels. * * sublet.show * => # */ VALUE subSubletVisibilityShow(VALUE self) { VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); data.l[0] = FIX2LONG(id); data.l[1] = SUB_EWMH_VISIBLE; subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_SUBLET_FLAGS", data, 32, True); return self; } /* }}} */ /* subSubletVisibilityHide {{{ */ /* * call-seq: hide -> Subtlext::Sublet * * Hide sublet from the panels. * * sublet.hide * => # */ VALUE subSubletVisibilityHide(VALUE self) { VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); data.l[0] = FIX2LONG(id); data.l[1] = SUB_EWMH_HIDDEN; subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_SUBLET_FLAGS", data, 32, True); return self; } /* }}} */ /* subSubletToString {{{ */ /* * call-seq: to_str -> String * * Convert Sublet object to string. * * puts sublet * => sublet */ VALUE subSubletToString(VALUE self) { VALUE name = Qnil; /* Check ruby object */ GET_ATTR(self, "@name", name); return name; } /* }}} */ /* subSubletKill {{{ */ /* * call-seq: kill -> nil * * Remove this Sublet from subtle and freeze this object. * * sublet.kill * => nil */ VALUE subSubletKill(VALUE self) { VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = FIX2INT(id); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_SUBLET_KILL", data, 32, True); rb_obj_freeze(self); ///< Freeze object return Qnil; } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/subtlext.c0000644000175000017500000020412611770332063020676 0ustar formorerformorer /** * @package subtlext * * @file Main functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/subtlext.c,v 3216 2012/06/15 17:18:12 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include #include #include "subtlext.h" #ifdef HAVE_X11_EXTENSIONS_XTEST_H #include #endif /* HAVE_X11_EXTENSIONS_XTEST_H */ Display *display = NULL; VALUE mod = Qnil; /* SubtlextStringify {{{ */ static void SubtlextStringify(char *string) { assert(string); /* Lowercase and replace strange characters */ while('\0' != *string) { *string = toupper(*string); if(!isalnum(*string)) *string = '_'; string++; } } /* }}} */ /* SubtlextSweep {{{ */ static void SubtlextSweep(void) { if(display) { XCloseDisplay(display); display = NULL; } } /* }}} */ /* SubtlextPidReader {{{ */ /* * call-seq: pid => Fixnum * * Get window pid * * object.pid * => 123 **/ static VALUE SubtlextPidReader(VALUE self) { Window win = None; VALUE pid = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Load on demand */ if(NIL_P((pid = rb_iv_get(self, "@pid")))) { int *id = NULL; /* Get pid */ if((id = (int *)subSharedPropertyGet(display, win, XA_CARDINAL, XInternAtom(display, "_NET_WM_PID", False), NULL))) { pid = INT2FIX(*id); rb_iv_set(self, "@pid", pid); free(id); } } return pid; } /* }}} */ /* SubtlextFlags {{{ */ static int SubtlextFlags(VALUE key, VALUE value, VALUE data) { VALUE *rargs = (VALUE *)data; /* Set flags */ if(CHAR2SYM("name") == key) rargs[0] = SUB_MATCH_NAME; else if(CHAR2SYM("instance") == key) rargs[0] = SUB_MATCH_INSTANCE; else if(CHAR2SYM("class") == key) rargs[0] = SUB_MATCH_CLASS; else if(CHAR2SYM("gravity") == key) rargs[0] = SUB_MATCH_GRAVITY; else if(CHAR2SYM("role") == key) rargs[0] = SUB_MATCH_ROLE; else if(CHAR2SYM("pid") == key) rargs[0] = SUB_MATCH_PID; /* Set value */ if(0 != rargs[0] && RTEST(value)) { rargs[1] = value; return ST_STOP; } return ST_CONTINUE; } /* }}} */ /* SubtlextStyle {{{ */ /* * call-seq: style=(string) -> nil * style=(symbol) -> nil * style=(nil) -> nil * * Set style state of this Object, use nil to reset state. * * object.style = :blue * => nil */ static VALUE SubtlextStyle(VALUE self, VALUE value) { char *prop = NULL; VALUE id = Qnil, str = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); /* Check object type */ if(rb_obj_is_instance_of(self, rb_const_get(mod, rb_intern("View")))) prop = "SUBTLE_VIEW_STYLE"; else prop = "SUBTLE_SUBLET_STYLE"; /* Check value type */ switch(rb_type(value)) { case T_SYMBOL: str = rb_sym_to_s(value); case T_STRING: snprintf(data.b, sizeof(data.b), "%d#%s", (int)FIX2INT(id), RSTRING_PTR(str)); subSharedMessage(display, ROOT, prop, data, 32, True); break; case T_NIL: snprintf(data.b, sizeof(data.b), "%d#", (int)FIX2INT(id)); subSharedMessage(display, ROOT, prop, data, 32, True); break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } return Qnil; } /* }}} */ /* SubtlextHash {{{ */ /* * call-seq: hash -> Hash * * Convert this object to hash. * * puts object.hash * => 1746246187916025425 */ static VALUE SubtlextHash(VALUE self) { VALUE str = Qnil, id = rb_intern("to_str"); /* Convert to string */ if(rb_respond_to(self, id)) str = rb_funcall(self, id, 0, Qnil); return T_STRING == rb_type(str) ? INT2FIX(rb_str_hash(str)) : Qnil; } /* }}} */ /* SubtlextXError {{{ */ static int SubtlextXError(Display *disp, XErrorEvent *ev) { #ifdef DEBUG if(42 != ev->request_code) /* X_SetInputFocus */ { char error[255] = { 0 }; XGetErrorText(disp, ev->error_code, error, sizeof(error)); printf(" %s: win=%#lx, request=%d\n", error, ev->resourceid, ev->request_code); } #endif /* DEBUG */ return 0; } /* }}} */ /* Tags */ /* SubtlextTagFind {{{ */ static int SubtlextTagFind(VALUE value) { int tags = 0; /* Check object type */ switch(rb_type(value)) { case T_SYMBOL: case T_STRING: { int id, flags = 0; char *string = NULL; /* Handle symbols and strings */ if(T_SYMBOL == rb_type(value)) { flags |= SUB_MATCH_EXACT; string = (char *)SYM2CHAR(value); } else string = RSTRING_PTR(value); /* Find tag and get id */ if(-1 != (id = subSubtlextFindString("SUBTLE_TAG_LIST", string, NULL, flags))) tags |= (1L << (id + 1)); } break; case T_OBJECT: /* Check instance type and fetch id */ if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("Tag")))) { VALUE id = Qnil; if(FIXNUM_P((id = rb_iv_get(value, "@id")))) tags |= (1L << (FIX2INT(id) + 1)); } break; default: break; } /* Check if tags were found */ if(0 == tags) rb_raise(rb_eStandardError, "Invalid tag"); return tags; } /* }}} */ /* SubtlextTag {{{ */ static VALUE SubtlextTag(VALUE self, VALUE value, int action) { SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); /* Check object type */ switch(rb_type(value)) { case T_SYMBOL: case T_STRING: case T_OBJECT: data.l[1] |= SubtlextTagFind(value); break; case T_ARRAY: { int i; VALUE entry = Qnil; /* Collect tags and raise if a tag wasn't found. Empty * arrays reset tags and never enter this loop */ for(i = 0; Qnil != (entry = rb_ary_entry(value, i)); ++i) data.l[1] |= SubtlextTagFind(entry); } break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } /* Get and update tag mask */ if(0 != action) { int tags = FIX2INT(rb_iv_get(self, "@tags")); /* Update masks */ if(1 == action) data.l[1] = tags | data.l[1]; else if(-1 == action) data.l[1] = tags & ~data.l[1]; } /* Send message based on object type */ if(rb_obj_is_instance_of(self, rb_const_get(mod, rb_intern("Client")))) { VALUE win = Qnil; GET_ATTR(self, "@win", win); data.l[0] = NUM2LONG(win); subSharedMessage(display, ROOT, "SUBTLE_CLIENT_TAGS", data, 32, True); } else { VALUE id = Qnil; GET_ATTR(self, "@id", id); data.l[0] = FIX2LONG(id); subSharedMessage(display, ROOT, "SUBTLE_VIEW_TAGS", data, 32, True); } return Qnil; } /* }}} */ /* SubtlextTagWriter {{{ */ /* * call-seq: tags=(value) -> nil * * Set or remove all tags at once * * # Set new tags * object.tags=([ #, # ]) * => nil * * # Remove all tags * object.tags=([]) * => nil */ static VALUE SubtlextTagWriter(VALUE self, VALUE value) { return SubtlextTag(self, value, 0); } /* }}} */ /* SubtlextTagReader {{{ */ /* * call-seq: tags -> Array * * Get list of tags for window * * object.tags * => [#, #] */ static VALUE SubtlextTagReader(VALUE self) { char **tags = NULL; int i, ntags = 0, value_tags = 0; VALUE method = Qnil, klass = Qnil, t = Qnil; VALUE array = rb_ary_new(); /* Check ruby object */ rb_check_frozen(self); /* Fetch data */ method = rb_intern("new"); klass = rb_const_get(mod, rb_intern("Tag")); value_tags = FIX2INT(rb_iv_get(self, "@tags")); /* Check results */ if((tags = subSharedPropertyGetStrings(display, ROOT, XInternAtom(display, "SUBTLE_TAG_LIST", False), &ntags))) { for(i = 0; i < ntags; i++) { if(value_tags & (1L << (i + 1))) { /* Create new tag */ t = rb_funcall(klass, method, 1, rb_str_new2(tags[i])); rb_iv_set(t, "@id", INT2FIX(i)); rb_ary_push(array, t); } } XFreeStringList(tags); } return array; } /* }}} */ /* SubtlextTagAdd {{{ */ /* * call-seq: tag(value) -> nil * +(value) -> nil * * Add an existing tag to window * * object.tag("subtle") * => nil * * object.tag([ #, # ]) * => nil * * object + "subtle" * => nil */ static VALUE SubtlextTagAdd(VALUE self, VALUE value) { return SubtlextTag(self, value, 1); } /* }}} */ /* SubtlextTagDel {{{ */ /* * call-seq: untag(value) -> nil * -(value) -> nil * * Remove an existing tag from window * * object.untag("subtle") * => nil * * object.untag([ #, # ]) * => nil * * object - "subtle" * => nil */ static VALUE SubtlextTagDel(VALUE self, VALUE value) { return SubtlextTag(self, value, -1); } /* }}} */ /* SubtlextTagAsk {{{ */ static VALUE SubtlextTagAsk(VALUE self, VALUE value) { VALUE sym = Qnil, tag = Qnil, ret = Qfalse; /* Check ruby object */ rb_check_frozen(self); /* Check value type */ switch(rb_type(value)) { case T_STRING: sym = CHAR2SYM(RSTRING_PTR(value)); break; case T_SYMBOL: case T_OBJECT: sym = value; break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } /* Find tag */ if(RTEST(tag = subTagSingFirst(Qnil, sym))) { VALUE id = Qnil, tags = Qnil; /* Get properties */ id = rb_iv_get(tag, "@id"); tags = rb_iv_get(self, "@tags"); if(FIX2INT(tags) & (1L << (FIX2INT(id) + 1))) ret = Qtrue; } return ret; } /* }}} */ /* SubtlextTagReload {{{ */ static VALUE SubtlextTagReload(VALUE self) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Send message */ data.l[0] = NUM2LONG(win); subSharedMessage(display, ROOT, "SUBTLE_CLIENT_RETAG", data, 32, True); return Qnil; } /* }}} */ /* Send button/key */ /* SubtlextSendButton {{{ */ /* * call-seq: send_button(button, x, y) -> Object * * Emulate a click on a window with optional button * and x/y position * * object.send_button * => nil * * object.send_button(2) * => Object */ static VALUE SubtlextSendButton(int argc, VALUE *argv, VALUE self) { Window subwin = None; XEvent event = { 0 }; VALUE button = Qnil, x = Qnil, y = Qnil, win = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); rb_scan_args(argc, argv, "03", &button, &x, &y); /* Assemble button event */ event.type = EnterNotify; event.xcrossing.window = NUM2LONG(win); event.xcrossing.root = ROOT; event.xcrossing.subwindow = NUM2LONG(win); event.xcrossing.same_screen = True; event.xcrossing.x = FIXNUM_P(x) ? FIX2INT(x) : 5; event.xcrossing.y = FIXNUM_P(y) ? FIX2INT(y) : 5; /* Translate window x/y to root x/y */ XTranslateCoordinates(display, event.xcrossing.window, event.xcrossing.root, event.xcrossing.x, event.xcrossing.y, &event.xcrossing.x_root, &event.xcrossing.y_root, &subwin); //XSetInputFocus(display, event.xany.window, RevertToPointerRoot, CurrentTime); XSendEvent(display, NUM2LONG(win), True, EnterWindowMask, &event); /* Send button press event */ event.type = ButtonPress; event.xbutton.button = FIXNUM_P(button) ? FIX2INT(button) : 1; XSendEvent(display, NUM2LONG(win), True, ButtonPressMask, &event); XFlush(display); usleep(12000); /* Send button release event */ event.type = ButtonRelease; XSendEvent(display, NUM2LONG(win), True, ButtonReleaseMask, &event); XFlush(display); return self; } /* }}} */ #ifdef HAVE_X11_EXTENSIONS_XTEST_H /* SubtlextSendModifier {{{ */ static void SubtlextSendModifier(unsigned long state, int press) { /* Send modifier press/release events */ if(state & ShiftMask) ///< Shift key { XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Shift_L), press, CurrentTime); } else if(state & ControlMask) ///< Ctrl key { XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Control_L), press, CurrentTime); } else if(state & Mod1Mask) ///< Alt key { XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Alt_L), press, CurrentTime); } else if(state & Mod3Mask) /// Mod key< { XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Meta_L), press, CurrentTime); } else if(state & Mod4Mask) ///< Super key { XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Super_L), press, CurrentTime); } else if(state & Mod5Mask) ///< Alt Gr key { XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_ISO_Level3_Shift), press, CurrentTime); } } /* }}} */ #endif /* HAVE_X11_EXTENSIONS_XTEST_H */ /* SubtlextSendKey {{{ */ /* * call-seq: send_key(key, x, y) -> Object * * Emulate a keypress on a window * * object.send_key("d") * => Object */ static VALUE SubtlextSendKey(int argc, VALUE *argv, VALUE self) { VALUE keys = Qnil, x = Qnil, y = Qnil, win = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); rb_scan_args(argc, argv, "12", &keys, &x, &y); /* Check object type */ if(T_STRING == rb_type(keys)) { int mouse = False; unsigned int code = 0, state = 0; char *tokens = NULL, *tok = NULL, *save = NULL; Window subwin = None; KeySym sym = None; XEvent event = { 0 }; /* Assemble enter event */ event.type = EnterNotify; event.xcrossing.window = NUM2LONG(win); event.xcrossing.root = ROOT; event.xcrossing.subwindow = NUM2LONG(win); event.xcrossing.same_screen = True; event.xcrossing.x = FIXNUM_P(x) ? FIX2INT(x) : 5; event.xcrossing.y = FIXNUM_P(y) ? FIX2INT(y) : 5; /* Translate window x/y to root x/y */ XTranslateCoordinates(display, event.xcrossing.window, event.xcrossing.root, event.xcrossing.x, event.xcrossing.y, &event.xcrossing.x_root, &event.xcrossing.y_root, &subwin); XSendEvent(display, NUM2LONG(win), True, EnterWindowMask, &event); /* Parse keys */ tokens = strdup(RSTRING_PTR(keys)); tok = strtok_r(tokens, " ", &save); while(tok) { /* Parse key chain */ if(NoSymbol == (sym = subSharedParseKey(display, tok, &code, &state, &mouse))) { rb_raise(rb_eStandardError, "Unknown key"); return Qnil; } /* Check mouse */ if(True == mouse) { rb_raise(rb_eNotImpError, "Use #send_button instead"); return Qnil; } #ifdef HAVE_X11_EXTENSIONS_XTEST_H XTestGrabControl(display, True); /* Send key press/release events */ SubtlextSendModifier(state, True); XTestFakeKeyEvent(display, code, True, CurrentTime); XTestFakeKeyEvent(display, code, False, CurrentTime); SubtlextSendModifier(state, False); XTestGrabControl(display, False); #else /* HAVE_X11_EXTENSIONS_XTEST_H */ /* Send key press event */ event.type = KeyPress; event.xkey.state = state; event.xkey.keycode = code; XSendEvent(display, NUM2LONG(win), True, KeyPressMask, &event); XFlush(display); usleep(12000); /* Send key release event */ event.type = KeyRelease; XSendEvent(display, NUM2LONG(win), True, KeyReleaseMask, &event); #endif /* HAVE_X11_EXTENSIONS_XTEST_H */ tok = strtok_r(NULL, " ", &save); } XFlush(display); free(tokens); } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(keys)); return self; } /* }}} */ /* Focus */ /* SubtlextFocus {{{ */ /* * call-seq: focus -> nil * * Set focus to window * * object.focus * => nil */ static VALUE SubtlextFocus(VALUE self) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Send message */ data.l[0] = NUM2LONG(win); subSharedMessage(display, ROOT, "_NET_ACTIVE_WINDOW", data, 32, True); return self; } /* }}} */ /* SubtlextAskFocus {{{ */ /* * call-seq: has_focus? -> true or false * * Check if window has focus * * object.focus? * => true * * object.focus? * => false */ static VALUE SubtlextAskFocus(VALUE self) { VALUE ret = Qfalse, win = Qnil; unsigned long *focus = NULL; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Fetch data */ if((focus = (unsigned long *)subSharedPropertyGet(display, ROOT, XA_WINDOW, XInternAtom(display, "_NET_ACTIVE_WINDOW", False), NULL))) { if(*focus == NUM2LONG(win)) ret = Qtrue; free(focus); } return ret; } /* }}} */ /* Properties */ /* SubtlextPropReader {{{ */ /* * call-seq: [value] -> String or Nil * * Get arbitrary persistent property string or symbol value * * object["wm"] * => "subtle" * * object[:wm] * => "subtle" */ static VALUE SubtlextPropReader(VALUE self, VALUE key) { char *prop = NULL; VALUE ret = Qnil; /* Check ruby object */ rb_check_frozen(self); /* Check object type */ switch(rb_type(key)) { case T_STRING: prop = RSTRING_PTR(key); break; case T_SYMBOL: prop = (char *)SYM2CHAR(key); break; default: rb_raise(rb_eArgError, "Unexpected key value type `%s'", rb_obj_classname(key)); return Qnil; } /* Check results */ if(prop) { char propname[255] = { 0 }, *name = NULL, *result = NULL; Window win = ROOT; VALUE val = Qnil; /* Sanitize property name */ name = strdup(prop); SubtlextStringify(name); /* Check object type */ if(rb_obj_is_instance_of(self, rb_const_get(mod, rb_intern("View")))) { GET_ATTR(self, "@name", val); snprintf(propname, sizeof(propname), "SUBTLE_PROPERTY_%s_%s", RSTRING_PTR(val), name); } else ///< Client { GET_ATTR(self, "@win", val); win = NUM2LONG(val); snprintf(propname, sizeof(propname), "SUBTLE_PROPERTY_%s", name); } /* Get actual property */ if((result = subSharedPropertyGet(display, win, XInternAtom(display, "UTF8_STRING", False), XInternAtom(display, propname, False), NULL))) { ret = rb_str_new2(result); free(result); } free(name); } return ret; } /* }}} */ /* SubtlextPropWriter {{{ */ /* * call-seq: [key]= value -> Nil * * Set arbitrary persistent property string or symbol value * * Symbols are implictly converted to string, to remove a property just * set it to +nil+. * * object["wm"] = "subtle" * => nil * * object[:wm] = "subtle" * => nil * * object[:wm] = nil * => nil */ static VALUE SubtlextPropWriter(VALUE self, VALUE key, VALUE value) { VALUE val = Qnil, str = value; char *prop = NULL, *name = NULL, propname[255] = { 0 }; Window win = ROOT; /* Check ruby object */ rb_check_frozen(self); /* Check object type */ switch(rb_type(key)) { case T_STRING: prop = RSTRING_PTR(key); break; case T_SYMBOL: prop = (char *)SYM2CHAR(key); break; default: rb_raise(rb_eArgError, "Unexpected key value-type `%s'", rb_obj_classname(key)); return Qnil; } /* Sanitize property name */ name = strdup(prop); SubtlextStringify(name); /* Assemble property name */ if(rb_obj_is_instance_of(self, rb_const_get(mod, rb_intern("View")))) { GET_ATTR(self, "@name", val); snprintf(propname, sizeof(propname), "SUBTLE_PROPERTY_%s_%s", RSTRING_PTR(val), name); } else ///< Client { GET_ATTR(self, "@win", val); win = NUM2LONG(val); snprintf(propname, sizeof(propname), "SUBTLE_PROPERTY_%s", name); } /* Check value type */ switch(rb_type(value)) { case T_SYMBOL: str = rb_sym_to_s(value); case T_STRING: XChangeProperty(display, win, XInternAtom(display, propname, False), XInternAtom(display, "UTF8_STRING", False), 8, PropModeReplace, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str)); break; case T_NIL: XDeleteProperty(display, win, XInternAtom(display, propname, False)); break; default: rb_raise(rb_eArgError, "Unexpected value value-type `%s'", rb_obj_classname(value)); } XSync(display, False); ///< Sync all changes if(name) free(name); return Qnil; } /* }}} */ /* Comparisons */ /* SubtlextEqual {{{ */ static VALUE SubtlextEqual(VALUE self, VALUE other, const char *attr, int check_type) { int ret = False; VALUE val1 = Qnil, val2 = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, attr, val1); GET_ATTR(other, attr, val2); /* Check ruby object types */ if(check_type) { ret = (rb_obj_class(self) == rb_obj_class(other) && val1 == val2); } else ret = (val1 == val2); return ret ? Qtrue : Qfalse; } /* }}} */ /* SubtlextSpaceship {{{ */ static VALUE SubtlextSpaceship(VALUE self, VALUE other, const char *attr) { VALUE val1 = Qnil, val2 = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, attr, val1); GET_ATTR(other, attr, val2); return INT2FIX(val1 < val2 ? -1 : (val1 == val2 ? 0 : 1)); } /* }}} */ /* SubtlextEqualId {{{ */ /* * call-seq: ==(other) -> True or False * * Whether both objects have the same values (based on id) * * object1 == object2 * => true */ static VALUE SubtlextEqualId(VALUE self, VALUE other) { return SubtlextEqual(self, other, "@id", False); } /* }}} */ /* SubtlextEqualWindow {{{ */ /* * call-seq: ==(other) -> True or False * * Whether both objects have the same values (based on win) * * object1 == object2 * => true */ static VALUE SubtlextEqualWindow(VALUE self, VALUE other) { return SubtlextEqual(self, other, "@win", False); } /* }}} */ /* SubtlextEqualTypedId {{{ */ /* * call-seq: eql?(other) -> True or False * * Whether both objects have the same values and types (based on id) * * object1.eql? object2 * => true */ static VALUE SubtlextEqualTypedId(VALUE self, VALUE other) { return SubtlextEqual(self, other, "@id", True); } /* }}} */ /* SubtlextEqualTypedWindow {{{ */ /* * call-seq: eql?(other) -> True or False * * Whether both objects have the same value and types (based on win) * * object1.eql? object2 * => true */ static VALUE SubtlextEqualTypedWindow(VALUE self, VALUE other) { return SubtlextEqual(self, other, "@win", True); } /* }}} */ /* SubtlextEqualSpaceWindow {{{ */ /* * call-seq: <=>(other) -> -1, 0 or 1 * * Whether both objects have the same value. Returns -1, 0 or 1 when self is * less than, equal to or grater than other. (based on win) * * object1 <=> object2 * => 0 */ static VALUE SubtlextEqualSpaceWindow(VALUE self, VALUE other) { return SubtlextSpaceship(self, other, "@win"); } /* }}} */ /* SubtlextEqualSpacePixel {{{ */ /* * call-seq: <=>(other) -> -1, 0 or 1 * * Whether both objects have the same value. Returns -1, 0 or 1 when self is * less than, equal to or grater than other. (based on pixel) * * object1 <=> object2 * => 0 */ static VALUE SubtlextEqualSpacePixel(VALUE self, VALUE other) { return SubtlextSpaceship(self, other, "@pixel"); } /* }}} */ /* SubtlextEqualSpacePixmap {{{ */ /* * call-seq: <=>(other) -> -1, 0 or 1 * * Whether both objects have the same value. Returns -1, 0 or 1 when self is * less than, equal to or grater than other. (based on pixmap) * * object1 <=> object2 * => 0 */ static VALUE SubtlextEqualSpacePixmap(VALUE self, VALUE other) { return SubtlextSpaceship(self, other, "@pixmap"); } /* }}} */ /* SubtlextEqualSpaceId {{{ */ /* * call-seq: <=>(other) -> -1, 0 or 1 * * Whether both objects have the same value. Returns -1, 0 or 1 when self is * less than, equal to or grater than other. (based on id) * * object1 <=> object2 * => 0 */ static VALUE SubtlextEqualSpaceId(VALUE self, VALUE other) { return SubtlextSpaceship(self, other, "@id"); } /* }}} */ /* SubtlextWindowMatch {{{ */ static int SubtlextWindowMatch(Window win, regex_t *preg, const char *source, char **name, int flags) { int ret = False; char *wminstance = NULL, *wmclass = NULL; /* Fetch when needed */ if(name || flags & (SUB_MATCH_INSTANCE|SUB_MATCH_CLASS)) subSharedPropertyClass(display, win, &wminstance, &wmclass); /* Check window WM_NAME */ if(!ret && flags & SUB_MATCH_NAME) { char *wmname = NULL; subSharedPropertyName(display, win, &wmname, "subtle"); if(wmname) { ret = (flags & SUB_MATCH_EXACT ? 0 == strcmp(source, wmname) : subSharedRegexMatch(preg, wmname)); free(wmname); } } /* Check window WM_CLASS */ if(!ret && flags & (SUB_MATCH_INSTANCE|SUB_MATCH_CLASS)) { /* Check instance */ if(wminstance && flags & SUB_MATCH_INSTANCE) { ret = (flags & SUB_MATCH_EXACT ? 0 == strcmp(source, wminstance) : subSharedRegexMatch(preg, wminstance)); } /* Check class */ if(!ret && wmclass && flags & SUB_MATCH_CLASS) { ret = (flags & SUB_MATCH_EXACT ? 0 == strcmp(source, wmclass) : subSharedRegexMatch(preg, wmclass)); free(wmclass); } } /* Check window role */ if(!ret && flags & SUB_MATCH_ROLE) { char *role = NULL; if((role = subSharedPropertyGet(display, win, XA_STRING, XInternAtom(display, "WM_WINDOW_ROLE", False), NULL))) { ret = (flags & SUB_MATCH_EXACT ? 0 == strcmp(source, role) : subSharedRegexMatch(preg, role)); free(role); } } /* Check window gravity */ if(!ret && flags & SUB_MATCH_GRAVITY) { int *gravity = NULL, ngravities = 0; char **gravities = NULL; /* Fetch gravities */ gravities = subSharedPropertyGetStrings(display, ROOT, XInternAtom(display, "SUBTLE_GRAVITY_LIST", False), &ngravities); gravity = (int *)subSharedPropertyGet(display, win, XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_GRAVITY", False), NULL); /* Finally compare gravities */ if(gravities && gravity && 0 <= *gravity && *gravity < ngravities) { ret = (flags & SUB_MATCH_EXACT ? 0 == strcmp(source, gravities[*gravity]) : subSharedRegexMatch(preg, gravities[*gravity])); } if(gravities) XFreeStringList(gravities); if(gravity) free(gravity); } /* Check window pid */ if(!ret && flags & SUB_MATCH_PID) { int *pid = NULL; /* Fetch pid from window */ if((pid = (int *)subSharedPropertyGet(display, win, XA_CARDINAL, XInternAtom(display, "_NET_WM_PID", False), NULL))) { char pidbuf[10] = { 0 }; /* Convert pid to string */ snprintf(pidbuf, sizeof(pidbuf), "%d", (int)*pid); ret = (flags & SUB_MATCH_EXACT ? 0 == strcmp(source, pidbuf) : subSharedRegexMatch(preg, pidbuf)); free(pid); } } /* Copy instance name */ if(ret && name) { *name = (char *)subSharedMemoryAlloc( strlen(wminstance) + 1, sizeof(char)); strncpy(*name, wminstance, strlen(wminstance)); } if(wminstance) free(wminstance); return ret; } /* }}} */ /* Exported */ /** subSubtlextConnect {{{ * @brief Open connection to X display * @param[in] display_string Display name **/ void subSubtlextConnect(char *display_string) { /* Open display */ if(!display) { if(!(display = XOpenDisplay(display_string))) rb_raise(rb_eStandardError, "Invalid display `%s'", display_string); XSetErrorHandler(SubtlextXError); if(!setlocale(LC_CTYPE, "")) XSupportsLocale(); /* Register sweeper */ atexit(SubtlextSweep); } } /* }}} */ /** subSubtlextBacktrace {{{ * @brief Print ruby backtrace **/ void subSubtlextBacktrace(void) { VALUE lasterr = Qnil; /* Get last error */ if(!NIL_P(lasterr = rb_gv_get("$!"))) { int i; VALUE message = Qnil, klass = Qnil, backtrace = Qnil, entry = Qnil; /* Fetching backtrace data */ message = rb_obj_as_string(lasterr); klass = rb_class_path(CLASS_OF(lasterr)); backtrace = rb_funcall(lasterr, rb_intern("backtrace"), 0, NULL); /* Print error and backtrace */ printf("%s: %s\n", RSTRING_PTR(klass), RSTRING_PTR(message)); for(i = 0; Qnil != (entry = rb_ary_entry(backtrace, i)); ++i) printf("\tfrom %s\n", RSTRING_PTR(entry)); } } /* }}} */ /** subSubtlextConcat {{{ * @brief Concat string2 to string1 * @param[inout] str1 First string * @param[in] str2 Second string * @return Concatted string **/ VALUE subSubtlextConcat(VALUE str1, VALUE str2) { VALUE ret = Qnil; /* Check values */ if(RTEST(str1) && RTEST(str2) && T_STRING == rb_type(str1)) { VALUE string = str2; /* Convert argument to string */ if(T_STRING != rb_type(str2) && rb_respond_to(str2, rb_intern("to_s"))) string = rb_funcall(str2, rb_intern("to_s"), 0, NULL); /* Concat strings */ if(T_STRING == rb_type(string)) ret = rb_str_cat(str1, RSTRING_PTR(string), RSTRING_LEN(string)); } else rb_raise(rb_eArgError, "Unexpected value type"); return ret; } /* }}} */ /** subSubtlextParse {{{ * @brief Parse finder values * @param[in] buf Passed buffer * @param[in] len Buffer length * @param[inout] flags Set flags **/ VALUE subSubtlextParse(VALUE value, char *buf, int len, int *flags) { VALUE ret = Qnil; /* Handle flags {{{ */ if(flags) { /* Set defaults */ *flags = (SUB_MATCH_INSTANCE|SUB_MATCH_CLASS); /* Set flags from hash */ if(T_HASH == rb_type(value)) { VALUE rargs[2] = { 0, Qnil }; rb_hash_foreach(value, SubtlextFlags, (VALUE)&rargs); *flags = (int)rargs[0]; value = rargs[1]; } } /* }}} */ /* Check object type */ switch(rb_type(value)) { case T_FIXNUM: /* {{{ */ snprintf(buf, len, "%d", (int)FIX2INT(value)); break; /* }}} */ case T_STRING: /* {{{ */ snprintf(buf, len, "%s", RSTRING_PTR(value)); break; /* }}} */ case T_SYMBOL: /* {{{ */ ret = value; *flags |= SUB_MATCH_EXACT; snprintf(buf, len, "%s", SYM2CHAR(value)); break; /* }}} */ case T_OBJECT: /* {{{ */ ret = value; break; /* }}} */ default: /* {{{ */ rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); /* }}} */ } return ret; } /* }}} */ /** subSubtlextOneOrMany {{{ * @brief Return one value or many in an array * @param[in] value Current value * @param[in] prev Previous value or array * @retval Object Just one value * @retval Array Many values **/ VALUE subSubtlextOneOrMany(VALUE value, VALUE prev) { VALUE ret = Qnil; /* Handle different value types */ switch(rb_type(prev)) { case T_NIL: ret = value; break; case T_ARRAY: /* Just append */ rb_ary_push(prev, value); ret = prev; break; case T_DATA: case T_OBJECT: { /* Create new array and add data */ ret = rb_ary_new(); rb_ary_push(ret, prev); rb_ary_push(ret, value); } } return ret; } /* }}} */ /** subSubtlextManyToOne {{{ * @brief Return one value or nil from array or the value * @param[in] value Given value * @retval Object Just one value * @retval nil Empty array **/ VALUE subSubtlextManyToOne(VALUE value) { VALUE ret = Qnil; /* Handle different value types */ if(T_ARRAY == rb_type(value)) { /* Just fetch first */ if(0 < RARRAY_LEN(value)) ret = rb_ary_entry(value, 0); } else ret = value; return ret; } /* }}} */ /** subSubtlextWindowList {{{ * @brief Get property window list * @param[in] prop_name Property name * @param[inout] size List length * @return Property list **/ Window * subSubtlextWindowList(char *prop_name, int *size) { Window *wins = NULL; unsigned long len = 0; assert(prop_name && size); /* Get property list */ if((wins = (Window *)subSharedPropertyGet(display, ROOT, XA_WINDOW, XInternAtom(display, prop_name, False), &len))) { if(size) *size = len; } else if(size) *size = 0; return wins; } /* }}} */ /** subSubtlextFindString {{{ * @brief Find string in property list * @param[in] prop_name Property name * @param[in] source Regexp source * @param[inout] name Found name * @param[in] flags Match flags * @retval -1 String not found * @retval >=0 Found id **/ int subSubtlextFindString(char *prop_name, char *source, char **name, int flags) { int ret = -1, size = 0; char **strings = NULL; regex_t *preg = NULL; assert(prop_name && source); /* Fetch data */ preg = subSharedRegexNew(source); strings = subSharedPropertyGetStrings(display, ROOT, XInternAtom(display, prop_name, False), &size); /* Check results */ if(preg && strings) { int selid = -1, i; /* Special values */ if(isdigit(source[0])) selid = atoi(source); for(i = 0; i < size; i++) { if(selid == i || (-1 == selid && ((flags & SUB_MATCH_EXACT && 0 == strcmp(source, strings[i])) || (preg && !(flags & SUB_MATCH_EXACT) && subSharedRegexMatch(preg, strings[i]))))) { if(name) *name = strdup(strings[i]); ret = i; break; } } } if(preg) subSharedRegexKill(preg); if(strings) XFreeStringList(strings); return ret; } /* }}} */ /** subSubtlextFindObjects {{{ * @brief Find match in propery list and create objects * @param[in] prop_name Property name * @param[in] class_name Class name * @param[in] source Regexp source * @param[in] flags Match flags * @param[in] first Return first or all * @retval Qnil No match * @retval Object One match * @retval Array Multiple matches **/ VALUE subSubtlextFindObjects(char *prop_name, char *class_name, char *source, int flags, int first) { int i, nstrings = 0; char **strings = NULL; VALUE ret = first ? Qnil : rb_ary_new(); assert(prop_name && class_name && source); /* Check results */ if((strings = subSharedPropertyGetStrings(display, ROOT, XInternAtom(display, prop_name, False), &nstrings))) { int selid = -1; VALUE meth_new = Qnil, meth_update = Qnil, klass = Qnil, obj = Qnil; regex_t *preg = subSharedRegexNew(source); /* Special values */ if(isdigit(source[0])) selid = atoi(source); /* Fetch data */ meth_new = rb_intern("new"); meth_update = rb_intern("update"); klass = rb_const_get(mod, rb_intern(class_name)); /* Check each string */ for(i = 0; i < nstrings; i++) { /* Check if string matches */ if(selid == i || (-1 == selid && ((flags & SUB_MATCH_EXACT && 0 == strcmp(source, strings[i])) || (preg && !(flags & SUB_MATCH_EXACT) && subSharedRegexMatch(preg, strings[i]))))) { /* Create new object */ if(RTEST((obj = rb_funcall(klass, meth_new, 1, rb_str_new2(strings[i]))))) { rb_iv_set(obj, "@id", INT2FIX(i)); /* Call update method of object */ if(rb_respond_to(obj, meth_update)) rb_funcall(obj, meth_update, 0, Qnil); /* Select first or many */ if(first) { ret = obj; break; } else ret = subSubtlextOneOrMany(obj, ret); } } } if(preg) subSharedRegexKill(preg); XFreeStringList(strings); } else rb_raise(rb_eStandardError, "Unknown property list `%s'", prop_name); return ret; } /* }}} */ /** subSubtlextFindWindows {{{ * @brief Find match in propery list and create objects * @param[in] prop_name Property name * @param[in] class_name Class name * @param[in] source Regexp source * @param[in] flags Match flags * @retval Qnil No match * @retval Object One match * @retval Array Multiple matches **/ VALUE subSubtlextFindWindows(char *prop_name, char *class_name, char *source, int flags, int first) { int i, size = 0; Window *wins = NULL; VALUE ret = first ? Qnil : rb_ary_new(); /* Get window list */ if((wins = subSubtlextWindowList(prop_name, &size))) { int selid = -1; Window selwin = None; VALUE meth_new = Qnil, meth_update = Qnil, klass = Qnil, obj = Qnil; regex_t *preg = NULL; /* Create regexp when required */ if(!(flags & SUB_MATCH_EXACT)) preg = subSharedRegexNew(source); /* Special values */ if(isdigit(source[0])) selid = atoi(source); if('#' == source[0]) selwin = subSubtleSingSelect(Qnil); /* Fetch data */ meth_new = rb_intern("new"); meth_update = rb_intern("update"); klass = rb_const_get(mod, rb_intern(class_name)); /* Check results */ for(i = 0; i < size; i++) { if(selid == i || selid == wins[i] || selwin == wins[i] || (-1 == selid && SubtlextWindowMatch(wins[i], preg, source, NULL, flags))) { /* Create new obj */ if(RTEST((obj = rb_funcall(klass, meth_new, 1, LONG2NUM(wins[i]))))) { /* Call update method of object */ rb_funcall(obj, meth_update, 0, Qnil); /* Select first or many */ if(first) { ret = obj; break; } else ret = subSubtlextOneOrMany(obj, ret); } } } if(preg) subSharedRegexKill(preg); free(wins); } return ret; } /* }}} */ /** subSubtlextFindObjectsGeometry {{{ * @brief Find match in propery list and create objects * @param[in] prop_name Property name * @param[in] class_name Class name * @param[in] source Regexp source * @param[in] flags Match flags * @param[in] first Return first or all * @retval Qnil No match * @retval Object One match * @retval Array Multiple matches **/ VALUE subSubtlextFindObjectsGeometry(char *prop_name, char *class_name, char *source, int flags, int first) { int nstrings = 0; char **strings = NULL; VALUE ret = first ? Qnil : rb_ary_new(); subSubtlextConnect(NULL); ///< Implicit open connection /* Get string list */ if((strings = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, prop_name, False), &nstrings))) { int i, selid = -1; XRectangle geometry = { 0 }; char buf[30] = { 0 }; VALUE klass_obj = Qnil, klass_geom = Qnil, meth = Qnil; VALUE obj = Qnil, geom = Qnil; regex_t *preg = NULL; /* Fetch data */ klass_obj = rb_const_get(mod, rb_intern(class_name)); klass_geom = rb_const_get(mod, rb_intern("Geometry")); meth = rb_intern("new"); /* Create if source is given */ if(source) { if(isdigit(source[0])) selid = atoi(source); preg = subSharedRegexNew(source); } /* Create object list */ for(i = 0; i < nstrings; i++) { sscanf(strings[i], "%hdx%hd+%hd+%hd#%s", &geometry.x, &geometry.y, &geometry.width, &geometry.height, buf); /* Check if string matches */ if(!source || (source && (selid == i || (-1 == selid && ((flags & SUB_MATCH_EXACT && 0 == strcmp(source, buf)) || (preg && !(flags & SUB_MATCH_EXACT) && subSharedRegexMatch(preg, buf))))))) { /* Create new object and geometry */ obj = rb_funcall(klass_obj, meth, 1, rb_str_new2(buf)); geom = rb_funcall(klass_geom, meth, 4, INT2FIX(geometry.x), INT2FIX(geometry.y), INT2FIX(geometry.width), INT2FIX(geometry.height)); rb_iv_set(obj, "@id", INT2FIX(i)); rb_iv_set(obj, "@geometry", geom); /* Select first or many */ if(first) { ret = obj; break; } else ret = subSubtlextOneOrMany(obj, ret); } } if(preg) subSharedRegexKill(preg); XFreeStringList(strings); } else rb_raise(rb_eStandardError, "Unknown property list `%s'", prop_name); return ret; } /* }}} */ /* Plugin */ /* Init_subtlext {{{ */ /* * Subtlext is the module of the extension */ void Init_subtlext(void) { VALUE client = Qnil, color = Qnil, geometry = Qnil, gravity = Qnil; VALUE icon = Qnil, screen = Qnil, subtle = Qnil, sublet = Qnil; VALUE tag = Qnil, tray = Qnil, view = Qnil, window = Qnil; /* * Document-class: Subtlext * * Subtlext is the toplevel module */ mod = rb_define_module("Subtlext"); /* Subtlext version */ rb_define_const(mod, "VERSION", rb_str_new2(PKG_VERSION)); /* * Document-class: Subtlext::Client * * Class for interaction with clients */ client = rb_define_class_under(mod, "Client", rb_cObject); /* Window id */ rb_define_attr(client, "win", 1, 0); /* WM_NAME */ rb_define_attr(client, "name", 1, 0); /* Instance of WM_CLASS */ rb_define_attr(client, "instance", 1, 0); /* Class of WM_CLASS */ rb_define_attr(client, "klass", 1, 0); /* Window role */ rb_define_attr(client, "role", 1, 0); /* Bitfield of window states */ rb_define_attr(client, "flags", 1, 0); /* Singleton methods */ rb_define_singleton_method(client, "select", subClientSingSelect, 0); rb_define_singleton_method(client, "find", subClientSingFind, 1); rb_define_singleton_method(client, "first", subClientSingFirst, 1); rb_define_singleton_method(client, "current", subClientSingCurrent, 0); rb_define_singleton_method(client, "visible", subClientSingVisible, 0); rb_define_singleton_method(client, "list", subClientSingList, 0); rb_define_singleton_method(client, "recent", subClientSingRecent, 0); /* General methods */ rb_define_method(client, "has_tag?", SubtlextTagAsk, 1); rb_define_method(client, "tags", SubtlextTagReader, 0); rb_define_method(client, "tags=", SubtlextTagWriter, 1); rb_define_method(client, "tag", SubtlextTagAdd, 1); rb_define_method(client, "untag", SubtlextTagDel, 1); rb_define_method(client, "retag", SubtlextTagReload, 0); rb_define_method(client, "send_button", SubtlextSendButton, -1); rb_define_method(client, "send_key", SubtlextSendKey, -1); rb_define_method(client, "focus", SubtlextFocus, 0); rb_define_method(client, "has_focus?", SubtlextAskFocus, 0); rb_define_method(client, "[]", SubtlextPropReader, 1); rb_define_method(client, "[]=", SubtlextPropWriter, 2); rb_define_method(client, "<=>", SubtlextEqualSpaceWindow, 1); rb_define_method(client, "==", SubtlextEqualWindow, 1); rb_define_method(client, "eql?", SubtlextEqualTypedWindow, 1); rb_define_method(client, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(client, "initialize", subClientInit, 1); rb_define_method(client, "update", subClientUpdate, 0); rb_define_method(client, "views", subClientViewList, 0); rb_define_method(client, "is_full?", subClientFlagsAskFull, 0); rb_define_method(client, "is_float?", subClientFlagsAskFloat, 0); rb_define_method(client, "is_stick?", subClientFlagsAskStick, 0); rb_define_method(client, "is_resize?", subClientFlagsAskResize, 0); rb_define_method(client, "is_urgent?", subClientFlagsAskUrgent, 0); rb_define_method(client, "is_zaphod?", subClientFlagsAskZaphod, 0); rb_define_method(client, "is_fixed?", subClientFlagsAskFixed, 0); rb_define_method(client, "is_borderless?", subClientFlagsAskBorderless, 0); rb_define_method(client, "toggle_full", subClientFlagsToggleFull, 0); rb_define_method(client, "toggle_float", subClientFlagsToggleFloat, 0); rb_define_method(client, "toggle_stick", subClientFlagsToggleStick, 0); rb_define_method(client, "toggle_resize", subClientFlagsToggleResize, 0); rb_define_method(client, "toggle_urgent", subClientFlagsToggleUrgent, 0); rb_define_method(client, "toggle_zaphod", subClientFlagsToggleZaphod, 0); rb_define_method(client, "toggle_fixed", subClientFlagsToggleFixed, 0); rb_define_method(client, "toggle_borderless", subClientFlagsToggleBorderless, 0); rb_define_method(client, "flags=", subClientFlagsWriter, 1); rb_define_method(client, "raise", subClientRestackRaise, 0); rb_define_method(client, "lower", subClientRestackLower, 0); rb_define_method(client, "to_str", subClientToString, 0); rb_define_method(client, "gravity", subClientGravityReader, 0); rb_define_method(client, "gravity=", subClientGravityWriter, 1); rb_define_method(client, "geometry", subClientGeometryReader, 0); rb_define_method(client, "geometry=", subClientGeometryWriter, -1); rb_define_method(client, "screen", subClientScreenReader, 0); rb_define_method(client, "pid", SubtlextPidReader, 0); rb_define_method(client, "alive?", subClientAskAlive, 0); rb_define_method(client, "kill", subClientKill, 0); /* Singleton aliases */ rb_define_alias(rb_singleton_class(client), "[]", "first"); rb_define_alias(rb_singleton_class(client), "all", "list"); /* Aliases */ rb_define_alias(client, "+", "tag"); rb_define_alias(client, "-", "untag"); rb_define_alias(client, "save", "update"); rb_define_alias(client, "to_s", "to_str"); rb_define_alias(client, "click", "send_button"); /* * Document-class: Subtlext::Color * * Color class for interaction with colors */ color = rb_define_class_under(mod, "Color", rb_cObject); /* Red fraction */ rb_define_attr(color, "red", 1, 0); /* Green fraction */ rb_define_attr(color, "green", 1, 0); /* Blue fraction */ rb_define_attr(color, "blue", 1, 0); /* Pixel number */ rb_define_attr(color, "pixel", 1, 0); /* General methods */ rb_define_method(color, "<=>", SubtlextEqualSpacePixel, 1); rb_define_method(color, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(color, "initialize", subColorInit, -1); rb_define_method(color, "to_hex", subColorToHex, 0); rb_define_method(color, "to_ary", subColorToArray, 0); rb_define_method(color, "to_hash", subColorToHash, 0); rb_define_method(color, "to_str", subColorToString, 0); rb_define_method(color, "+", subColorOperatorPlus, 1); rb_define_method(color, "==", subColorEqual, 1); rb_define_method(color, "eql?", subColorEqualTyped, 1); /* Aliases */ rb_define_alias(color, "to_a", "to_ary"); rb_define_alias(color, "to_h", "to_hash"); rb_define_alias(color, "to_s", "to_str"); /* * Document-class: Subtlext::Geometry * * Class for various sizes */ geometry = rb_define_class_under(mod, "Geometry", rb_cObject); /* X offset */ rb_define_attr(geometry, "x", 1, 1); /* Y offset */ rb_define_attr(geometry, "y", 1, 1); /* Geometry width */ rb_define_attr(geometry, "width", 1, 1); /* Geometry height */ rb_define_attr(geometry, "height", 1, 1); /* General methods */ rb_define_method(geometry, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(geometry, "initialize", subGeometryInit, -1); rb_define_method(geometry, "to_ary", subGeometryToArray, 0); rb_define_method(geometry, "to_hash", subGeometryToHash, 0); rb_define_method(geometry, "to_str", subGeometryToString, 0); rb_define_method(geometry, "==", subGeometryEqual, 1); rb_define_method(geometry, "eql?", subGeometryEqualTyped, 1); /* Aliases */ rb_define_alias(geometry, "to_a", "to_ary"); rb_define_alias(geometry, "to_h", "to_hash"); rb_define_alias(geometry, "to_s", "to_str"); /* * Document-class: Subtlext::Gravity * * Class for Client placement */ gravity = rb_define_class_under(mod, "Gravity", rb_cObject); /* Gravity id */ rb_define_attr(gravity, "id", 1, 0); /* Name of the gravity */ rb_define_attr(gravity, "name", 1, 0); /* Geometry */ rb_define_attr(gravity, "geometry", 1, 0); /* Singleton methods */ rb_define_singleton_method(gravity, "find", subGravitySingFind, 1); rb_define_singleton_method(gravity, "first", subGravitySingFirst, 1); rb_define_singleton_method(gravity, "list", subGravitySingList, 0); /* General methods */ rb_define_method(gravity, "<=>", SubtlextEqualSpaceId, 1); rb_define_method(gravity, "==", SubtlextEqualId, 1); rb_define_method(gravity, "eql?", SubtlextEqualTypedId, 1); rb_define_method(gravity, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(gravity, "initialize", subGravityInit, -1); rb_define_method(gravity, "save", subGravitySave, 0); rb_define_method(gravity, "clients", subGravityClients, 0); rb_define_method(gravity, "geometry_for", subGravityGeometryFor, 1); rb_define_method(gravity, "geometry", subGravityGeometryReader, 0); rb_define_method(gravity, "geometry=", subGravityGeometryWriter, -1); rb_define_method(gravity, "tiling=", subGravityTilingWriter, 1); rb_define_method(gravity, "to_str", subGravityToString, 0); rb_define_method(gravity, "to_sym", subGravityToSym, 0); rb_define_method(gravity, "kill", subGravityKill, 0); /* Singleton aliases */ rb_define_alias(rb_singleton_class(gravity), "[]", "first"); rb_define_alias(rb_singleton_class(gravity), "all", "list"); /* Aliases */ rb_define_alias(gravity, "to_s", "to_str"); /* * Document-class: Subtlext::Icon * * Icon class for interaction with icons */ icon = rb_define_class_under(mod, "Icon", rb_cObject); /* Icon width */ rb_define_attr(icon, "width", 1, 0); /* Icon height */ rb_define_attr(icon, "height", 1, 0); /* Icon pixmap idt */ rb_define_attr(icon, "pixmap", 1, 0); /* Allocate */ rb_define_alloc_func(icon, subIconAlloc); /* General methods */ rb_define_method(icon, "<=>", SubtlextEqualSpacePixmap, 1); rb_define_method(icon, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(icon, "initialize", subIconInit, -1); rb_define_method(icon, "draw_point", subIconDrawPoint, -1); rb_define_method(icon, "draw_line", subIconDrawLine, -1); rb_define_method(icon, "draw_rect", subIconDrawRect, -1); rb_define_method(icon, "copy_area", subIconCopyArea, -1); rb_define_method(icon, "clear", subIconClear, -1); rb_define_method(icon, "bitmap?", subIconAskBitmap, 0); rb_define_method(icon, "to_str", subIconToString, 0); rb_define_method(icon, "+", subIconOperatorPlus, 1); rb_define_method(icon, "*", subIconOperatorMult, 1); rb_define_method(icon, "==", subIconEqual, 1); rb_define_method(icon, "eql?", subIconEqualTyped, 1); /* Aliases */ rb_define_alias(icon, "to_s", "to_str"); rb_define_alias(icon, "draw", "draw_point"); /* * Document-class: Subtlext::Screen * * Class for interaction with screens */ screen = rb_define_class_under(mod, "Screen", rb_cObject); /* Screen id */ rb_define_attr(screen, "id", 1, 0); /* Geometry */ rb_define_attr(screen, "geometry", 1, 0); /* Singleton methods */ rb_define_singleton_method(screen, "find", subScreenSingFind, 1); rb_define_singleton_method(screen, "list", subScreenSingList, 0); rb_define_singleton_method(screen, "current", subScreenSingCurrent, 0); /* General methods */ rb_define_method(screen, "<=>", SubtlextEqualSpaceId, 1); rb_define_method(screen, "==", SubtlextEqualId, 1); rb_define_method(screen, "eql?", SubtlextEqualTypedId, 1); rb_define_method(screen, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(screen, "initialize", subScreenInit, 1); rb_define_method(screen, "update", subScreenUpdate, 0); rb_define_method(screen, "jump", subScreenJump, 0); rb_define_method(screen, "view", subScreenViewReader, 0); rb_define_method(screen, "view=", subScreenViewWriter, 1); rb_define_method(screen, "current?", subScreenAskCurrent, 0); rb_define_method(screen, "to_str", subScreenToString, 0); /* Singleton aliases */ rb_define_alias(rb_singleton_class(screen), "[]", "find"); rb_define_alias(rb_singleton_class(screen), "all", "list"); /* Aliases */ rb_define_alias(screen, "save", "update"); rb_define_alias(screen, "to_s", "to_str"); /* * Document-class: Subtlext::Subtle * * Module for interaction with the window manager */ subtle = rb_define_module_under(mod, "Subtle"); /* Singleton methods */ rb_define_singleton_method(subtle, "display", subSubtleSingDisplayReader, 0); rb_define_singleton_method(subtle, "display=", subSubtleSingDisplayWriter, 1); rb_define_singleton_method(subtle, "select_window", subSubtleSingSelect, 0); rb_define_singleton_method(subtle, "running?", subSubtleSingAskRunning, 0); rb_define_singleton_method(subtle, "render", subSubtleSingRender, 0); rb_define_singleton_method(subtle, "reload", subSubtleSingReload, 0); rb_define_singleton_method(subtle, "restart", subSubtleSingRestart, 0); rb_define_singleton_method(subtle, "quit", subSubtleSingQuit, 0); rb_define_singleton_method(subtle, "colors", subSubtleSingColors, 0); rb_define_singleton_method(subtle, "font", subSubtleSingFont, 0); rb_define_singleton_method(subtle, "spawn", subSubtleSingSpawn, 1); /* Aliases */ rb_define_alias(rb_singleton_class(subtle), "reload_config", "reload"); /* * Document-class: Subtlext::Sublet * * Class for interaction with sublets */ sublet = rb_define_class_under(mod, "Sublet", rb_cObject); /* Sublet id */ rb_define_attr(sublet, "id", 1, 0); /* Name of the sublet */ rb_define_attr(sublet, "name", 1, 0); /* Geometry */ rb_define_attr(sublet, "geometry", 1, 0); /* Singleton methods */ rb_define_singleton_method(sublet, "find", subSubletSingFind, 1); rb_define_singleton_method(sublet, "first", subSubletSingFirst, 1); rb_define_singleton_method(sublet, "list", subSubletSingList, 0); /* General methods */ rb_define_method(sublet, "<=>", SubtlextEqualSpaceId, 1); rb_define_method(sublet, "==", SubtlextEqualId, 1); rb_define_method(sublet, "eql?", SubtlextEqualTypedId, 1); rb_define_method(sublet, "style=", SubtlextStyle, 1); rb_define_method(sublet, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(sublet, "initialize", subSubletInit, 1); rb_define_method(sublet, "update", subSubletUpdate, 0); rb_define_method(sublet, "send_data", subSubletSend, 1); rb_define_method(sublet, "show", subSubletVisibilityShow, 0); rb_define_method(sublet, "hide", subSubletVisibilityHide, 0); rb_define_method(sublet, "to_str", subSubletToString, 0); rb_define_method(sublet, "kill", subSubletKill, 0); /* Singleton aliases */ rb_define_alias(rb_singleton_class(sublet), "[]", "first"); rb_define_alias(rb_singleton_class(sublet), "all", "list"); /* Aliases */ rb_define_alias(sublet, "to_s", "to_str"); /* * Document-class: Subtlext::Tag * * Class for interaction with tags */ tag = rb_define_class_under(mod, "Tag", rb_cObject); /* Tag id */ rb_define_attr(tag, "id", 1, 0); /* Name of the tag */ rb_define_attr(tag, "name", 1, 0); /* Singleton methods */ rb_define_singleton_method(tag, "find", subTagSingFind, 1); rb_define_singleton_method(tag, "first", subTagSingFirst, 1); rb_define_singleton_method(tag, "visible", subTagSingVisible, 0); rb_define_singleton_method(tag, "list", subTagSingList, 0); /* General methods */ rb_define_method(tag, "<=>", SubtlextEqualSpaceId, 1); rb_define_method(tag, "==", SubtlextEqualId, 1); rb_define_method(tag, "eql?", SubtlextEqualTypedId, 1); rb_define_method(tag, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(tag, "initialize", subTagInit, 1); rb_define_method(tag, "save", subTagSave, 0); rb_define_method(tag, "clients", subTagClients, 0); rb_define_method(tag, "views", subTagViews, 0); rb_define_method(tag, "to_str", subTagToString, 0); rb_define_method(tag, "kill", subTagKill, 0); /* Singleton aliases */ rb_define_alias(rb_singleton_class(tag), "[]", "first"); rb_define_alias(rb_singleton_class(tag), "all", "list"); /* Aliases */ rb_define_alias(tag, "to_s", "to_str"); /* * Document-class: Subtlext::Tray * * Class for interaction with trays */ tray = rb_define_class_under(mod, "Tray", rb_cObject); /* Window id */ rb_define_attr(tray, "win", 1, 0); /* WM_NAME */ rb_define_attr(tray, "name", 1, 0); /* Instance of WM_CLASS */ rb_define_attr(tray, "instance", 1, 0); /* Class of WM_CLASS */ rb_define_attr(tray, "klass", 1, 0); /* Singleton methods */ rb_define_singleton_method(tray, "find", subTraySingFind, 1); rb_define_singleton_method(tray, "first", subTraySingFirst, 1); rb_define_singleton_method(tray, "list", subTraySingList, 0); /* General methods */ rb_define_method(tray, "send_button", SubtlextSendButton, -1); rb_define_method(tray, "send_key", SubtlextSendKey, -1); rb_define_method(tray, "focus", SubtlextFocus, 0); rb_define_method(tray, "has_focus?", SubtlextAskFocus, 0); rb_define_method(tray, "[]", SubtlextPropReader, 1); rb_define_method(tray, "[]=", SubtlextPropWriter, 2); rb_define_method(tray, "<=>", SubtlextEqualSpaceWindow, 1); rb_define_method(tray, "==", SubtlextEqualWindow, 1); rb_define_method(tray, "eql?", SubtlextEqualTypedWindow, 1); rb_define_method(tray, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(tray, "initialize", subTrayInit, 1); rb_define_method(tray, "update", subTrayUpdate, 0); rb_define_method(tray, "pid", SubtlextPidReader, 0); rb_define_method(tray, "to_str", subTrayToString, 0); rb_define_method(tray, "kill", subTrayKill, 0); /* Singleton aliases */ rb_define_alias(rb_singleton_class(tray), "[]", "first"); rb_define_alias(rb_singleton_class(tray), "all", "list"); /* Aliases */ rb_define_alias(tray, "to_s", "to_str"); rb_define_alias(tray, "click", "send_button"); /* * Document-class: Subtlext::View * * Class for interaction with views */ view = rb_define_class_under(mod, "View", rb_cObject); /* View id */ rb_define_attr(view, "id", 1, 0); /* Name of the view */ rb_define_attr(view, "name", 1, 0); /* Singleton methods */ rb_define_singleton_method(view, "find", subViewSingFind, 1); rb_define_singleton_method(view, "first", subViewSingFirst, 1); rb_define_singleton_method(view, "current", subViewSingCurrent, 0); rb_define_singleton_method(view, "visible", subViewSingVisible, 0); rb_define_singleton_method(view, "list", subViewSingList, 0); /* General methods */ rb_define_method(view, "has_tag?", SubtlextTagAsk, 1); rb_define_method(view, "tags", SubtlextTagReader, 0); rb_define_method(view, "tags=", SubtlextTagWriter, 1); rb_define_method(view, "tag", SubtlextTagAdd, 1); rb_define_method(view, "untag", SubtlextTagDel, 1); rb_define_method(view, "[]", SubtlextPropReader, 1); rb_define_method(view, "[]=", SubtlextPropWriter, 2); rb_define_method(view, "<=>", SubtlextEqualSpaceId, 1); rb_define_method(view, "==", SubtlextEqualId, 1); rb_define_method(view, "eql?", SubtlextEqualTypedId, 1); rb_define_method(view, "style=", SubtlextStyle, 1); rb_define_method(view, "hash", SubtlextHash, 0); /* Class methods */ rb_define_method(view, "initialize", subViewInit, 1); rb_define_method(view, "update", subViewUpdate, 0); rb_define_method(view, "save", subViewSave, 0); rb_define_method(view, "clients", subViewClients, 0); rb_define_method(view, "jump", subViewJump, 0); rb_define_method(view, "next", subViewSelectNext, 0); rb_define_method(view, "prev", subViewSelectPrev, 0); rb_define_method(view, "current?", subViewAskCurrent, 0); rb_define_method(view, "icon", subViewIcon, 0); rb_define_method(view, "to_str", subViewToString, 0); rb_define_method(view, "kill", subViewKill, 0); /* Singleton aliases */ rb_define_alias(rb_singleton_class(view), "[]", "first"); rb_define_alias(rb_singleton_class(view), "all", "list"); /* Aliases */ rb_define_alias(view, "+", "tag"); rb_define_alias(view, "-", "untag"); rb_define_alias(view, "click", "jump"); rb_define_alias(view, "to_s", "to_str"); /* * Document-class: Subtlext::Window * * Class for interaction with windows */ window = rb_define_class_under(mod, "Window", rb_cObject); /* Window id */ rb_define_attr(window, "win", 1, 0); /* Window visibility */ rb_define_attr(window, "hidden", 1, 0); /* Allocate */ rb_define_alloc_func(window, subWindowAlloc); /* Singleton methods */ rb_define_singleton_method(window, "once", subWindowSingOnce, 1); /* General methods */ rb_define_method(window, "send_button", SubtlextSendButton, -1); rb_define_method(window, "send_key", SubtlextSendKey, -1); rb_define_method(window, "focus", SubtlextFocus, 0); rb_define_method(window, "[]", SubtlextPropReader, 1); rb_define_method(window, "[]=", SubtlextPropWriter, 2); rb_define_method(window, "<=>", SubtlextEqualSpaceWindow, 1); rb_define_method(window, "==", SubtlextEqualWindow, 1); rb_define_method(window, "eql?", SubtlextEqualTypedWindow, 1); /* Class methods */ rb_define_method(window, "initialize", subWindowInit, 1); rb_define_method(window, "subwindow", subWindowSubwindow, 1); rb_define_method(window, "name=", subWindowNameWriter, 1); rb_define_method(window, "font=", subWindowFontWriter, 1); rb_define_method(window, "font_y", subWindowFontYReader, 0); rb_define_method(window, "font_height", subWindowFontHeightReader, 0); rb_define_method(window, "font_width", subWindowFontWidth, 1); rb_define_method(window, "foreground=", subWindowForegroundWriter, 1); rb_define_method(window, "background=", subWindowBackgroundWriter, 1); rb_define_method(window, "border_color=", subWindowBorderColorWriter, 1); rb_define_method(window, "border_size=", subWindowBorderSizeWriter, 1); rb_define_method(window, "on", subWindowOn, -1); rb_define_method(window, "draw_point", subWindowDrawPoint, -1); rb_define_method(window, "draw_line", subWindowDrawLine, -1); rb_define_method(window, "draw_rect", subWindowDrawRect, -1); rb_define_method(window, "draw_text", subWindowDrawText, -1); rb_define_method(window, "draw_icon", subWindowDrawIcon, -1); rb_define_method(window, "clear", subWindowClear, -1); rb_define_method(window, "redraw", subWindowRedraw, 0); rb_define_method(window, "geometry", subWindowGeometryReader, 0); rb_define_method(window, "geometry=", subWindowGeometryWriter, 1); rb_define_method(window, "raise", subWindowRaise, 0); rb_define_method(window, "lower", subWindowLower, 0); rb_define_method(window, "show", subWindowShow, 0); rb_define_method(window, "hide", subWindowHide, 0); rb_define_method(window, "hidden?", subWindowAskHidden, 0); rb_define_method(window, "kill", subWindowKill, 0); /* Singleton aliases */ rb_define_alias(rb_singleton_class(window), "configure", "new"); /* Aliases */ rb_define_alias(window, "click", "send_button"); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/gravity.c0000644000175000017500000003504311770332063020511 0ustar formorerformorer /** * @package subtlext * * @file Gravity functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/gravity.c,v 3216 2012/06/15 17:18:12 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* GravityToRect {{{ */ void GravityToRect(VALUE self, XRectangle *r) { VALUE geometry = rb_iv_get(self, "@geometry"); subGeometryToRect(geometry, r); ///< Get values } /* }}} */ /* GravityFindId {{{ */ static int GravityFindId(char *match, char **name, XRectangle *geometry) { int ret = -1, ngravities = 0; char **gravities = NULL; assert(match); /* Find gravity id */ if((gravities = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "SUBTLE_GRAVITY_LIST", False), &ngravities))) { int i; XRectangle geom = { 0 }; char buf[30] = { 0 }; for(i = 0; i < ngravities; i++) { sscanf(gravities[i], "%hdx%hd+%hd+%hd#%s", &geom.x, &geom.y, &geom.width, &geom.height, buf); /* Check id and name */ if((isdigit(match[0]) && atoi(match) == i) || (!isdigit(match[0]) && 0 == strcmp(match, buf))) { if(geometry) *geometry = geom; if(name) { *name = (char *)subSharedMemoryAlloc(strlen(buf) + 1, sizeof(char)); strncpy(*name, buf, strlen(buf)); } ret = i; break; } } } if(gravities) XFreeStringList(gravities); return ret; } /* }}} */ /* GravityFind {{{ */ static VALUE GravityFind(VALUE value, int first) { int flags = 0; VALUE parsed = Qnil; char buf[50] = { 0 }; subSubtlextConnect(NULL); ///< Implicit open connection /* Check object type */ switch(rb_type(parsed = subSubtlextParse( value, buf, sizeof(buf), &flags))) { case T_SYMBOL: if(CHAR2SYM("all") == parsed) return subGravitySingList(Qnil); break; case T_OBJECT: if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("Gravity")))) return parsed; } return subSubtlextFindObjectsGeometry("SUBTLE_GRAVITY_LIST", "Gravity", buf, flags, first); } /* }}} */ /* Singleton */ /* subGravitySingFind {{{ */ /* * call-seq: find(value) -> Array * [value] -> Array * * Find Gravity by a given value which can be of following type: * * [Fixnum] Array index of the SUBTLE_GRAVITY_LIST property list. * [String] Regexp match against name of Gravities, returns a Gravity on single * match or an Array on multiple matches. * [Symbol] Either :all for an array of all Views or any string for an * exact match. * * Subtlext::Gravity.find(1) * => [#] * * Subtlext::Gravity.find("subtle") * => [#] * * Subtlext::Gravity[".*"] * => [#, #] * * Subtlext::Gravity["subtle"] * => [] * * Subtlext::Gravity[:center] * => [#] */ VALUE subGravitySingFind(VALUE self, VALUE value) { return GravityFind(value, False); } /* }}} */ /* subGravitySingFirst {{{ */ /* * call-seq: first(value) -> Subtlext::Gravity or nil * * Find first Gravity by a given value which can be of following type: * * [Fixnum] Array index of the SUBTLE_GRAVITY_LIST property list. * [String] Regexp match against name of Gravities, returns a Gravity on single * match or an Array on multiple matches. * [Symbol] Either :all for an array of all Views or any string for an * exact match. * * Subtlext::Gravity.first(1) * => # * * Subtlext::Gravity.first("subtle") * => # */ VALUE subGravitySingFirst(VALUE self, VALUE value) { return GravityFind(value, True); } /* }}} */ /* subGravitySingList {{{ */ /* * call-seq: list -> Array * * Get an array of all Gravities based on the SUBTLE_GRAVITIY_LIST * property list. * * Subtlext::Gravity.list * => [#, #] * * Subtlext::Gravity.list * => [] */ VALUE subGravitySingList(VALUE self) { return subSubtlextFindObjectsGeometry("SUBTLE_GRAVITY_LIST", "Gravity", NULL, 0, False); } /* }}} */ /* Helper */ /* subGravityInstantiate {{{ */ VALUE subGravityInstantiate(char *name) { VALUE klass = Qnil, gravity = Qnil; /* Create new instance */ klass = rb_const_get(mod, rb_intern("Gravity")); gravity = rb_funcall(klass, rb_intern("new"), 1, rb_str_new2(name)); return gravity; } /* }}} */ /* Class */ /* subGravityInit {{{ */ /* * call-seq: new(name, x, y, width, height) -> Subtlext::Gravity * new(name, array) -> Subtlext::Gravity * new(name, hash) -> Subtlext::Gravity * new(name, string) -> Subtlext::Gravity * new(name, geometry) -> Subtlext::Gravity * * Create a new Gravity object locally without calling * #save automatically. * * The Gravity won't be useable until #save is called. * * gravity = Subtlext::Gravity.new("center", 0, 0, 100, 100) * => # */ VALUE subGravityInit(int argc, VALUE *argv, VALUE self) { VALUE data[5] = { Qnil }, geom = Qnil; rb_scan_args(argc, argv, "14", &data[0], &data[1], &data[2], &data[3], &data[4]); /* Check gravity name */ if(T_STRING != rb_type(data[0])) rb_raise(rb_eArgError, "Invalid value type"); /* Delegate arguments */ if(RTEST(data[1])) { VALUE klass = Qnil; klass = rb_const_get(mod, rb_intern("Geometry")); geom = rb_funcall2(klass, rb_intern("new"), argc - 1, argv + 1); } /* Init object */ rb_iv_set(self, "@id", Qnil); rb_iv_set(self, "@name", data[0]); rb_iv_set(self, "@geometry", geom); subSubtlextConnect(NULL); ///< Implicit open connection return self; } /* }}} */ /* subGravitySave {{{ */ /* * call-seq: save -> Subtlext::Gravity * * Save new Gravity object. * * gravity.save * => nil */ VALUE subGravitySave(VALUE self) { int id = -1; XRectangle geom = { 0 }; char *name = NULL; VALUE match = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@name", match); /* Find gravity */ if(-1 == (id = GravityFindId(RSTRING_PTR(match), &name, &geom))) { SubMessageData data = { { 0, 0, 0, 0, 0 } }; VALUE geometry = rb_iv_get(self, "@geometry"); if(NIL_P(geometry = rb_iv_get(self, "@geometry"))) rb_raise(rb_eStandardError, "No geometry given"); subGeometryToRect(geometry, &geom); ///< Get values /* Create new gravity */ snprintf(data.b, sizeof(data.b), "%hdx%hd+%hd+%hd#%s", geom.x, geom.y, geom.width, geom.height, RSTRING_PTR(match)); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_GRAVITY_NEW", data, 8, True); id = GravityFindId(RSTRING_PTR(match), NULL, NULL); } else ///< Update gravity { VALUE geometry = Qnil; geometry = subGeometryInstantiate(geom.x, geom.y, geom.width, geom.height); rb_iv_set(self, "@name", rb_str_new2(name)); rb_iv_set(self, "@gravity", geometry); free(name); } /* Guess gravity id */ if(-1 == id) { int ngravities = 0; char **gravities = NULL; gravities = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "SUBTLE_GRAVITY_LIST", False), &ngravities); id = ngravities; ///< New id should be last XFreeStringList(gravities); } /* Set properties */ rb_iv_set(self, "@id", INT2FIX(id)); return self; } /* }}} */ /* subGravityClients {{{ */ /* * call-seq: clients -> Array * * Get an array of Clients that have this Gravity. * * gravity.clients * => [#, #] * * tag.clients * => [] */ VALUE subGravityClients(VALUE self) { int i, nclients = 0; Window *clients = NULL; VALUE id = Qnil, klass = Qnil, meth = Qnil, array = Qnil, c = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ klass = rb_const_get(mod, rb_intern("Client")); meth = rb_intern("new"); array = rb_ary_new(); clients = subSubtlextWindowList("_NET_CLIENT_LIST", &nclients); /* Check results */ if(clients) { for(i = 0; i < nclients; i++) { unsigned long *gravity = NULL; /* Get window gravity */ gravity = (unsigned long *)subSharedPropertyGet(display, clients[i], XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_GRAVITY", False), NULL); /* Check if there are common tags or window is stick */ if(gravity && FIX2INT(id) == *gravity && !NIL_P(c = rb_funcall(klass, meth, 1, INT2FIX(i)))) { rb_iv_set(c, "@win", LONG2NUM(clients[i])); subClientUpdate(c); rb_ary_push(array, c); } if(gravity) free(gravity); } free(clients); } return array; } /* }}} */ /* subGravityGeometryFor {{{ */ /* * call-seq: geometry_for(screen) -> Subtlext::Geometry * * Get the Gravity Geometry for given Screen in pixel values. * * gravity.geometry_for(screen) * => # */ VALUE subGravityGeometryFor(VALUE self, VALUE value) { VALUE geom = Qnil; /* Check object instance */ if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("Screen")))) { XRectangle real = { 0 }, geom_grav = { 0 }, geom_screen = { 0 }; GravityToRect(self, &geom_grav); GravityToRect(value, &geom_screen); /* Calculate real values for screen */ real.width = geom_screen.width * geom_grav.width / 100; real.height = geom_screen.height * geom_grav.height / 100; real.x = geom_screen.x + (geom_screen.width - real.width) * geom_grav.x / 100; real.y = geom_screen.y + (geom_screen.height - real.height) * geom_grav.y / 100; geom = subGeometryInstantiate(real.x, real.y, real.width, real.height); } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); return geom; } /* }}} */ /* subGravityGeometryReader {{{ */ /* * call-seq: geometry -> Subtlext::Geometry * * Get the Gravity Geometry * * gravity.geometry * => # */ VALUE subGravityGeometryReader(VALUE self) { VALUE geometry = Qnil, name = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@name", name); /* Load on demand */ if(NIL_P((geometry = rb_iv_get(self, "@geometry")))) { XRectangle geom = { 0 }; GravityFindId(RSTRING_PTR(name), NULL, &geom); geometry = subGeometryInstantiate(geom.x, geom.y, geom.width, geom.height); rb_iv_set(self, "@geometry", geometry); } return geometry; } /* }}} */ /* subGravityGeometryWriter {{{ */ /* * call-seq: geometry=(x, y, width, height) -> Fixnum * geometry=(array) -> Array * geometry=(hash) -> Hash * geometry=(string) -> String * geometry=(geometry) -> Subtlext::Geometry * * Set the Gravity Geometry * * gravity.geometry = 0, 0, 100, 100 * => 0 * * gravity.geometry = [ 0, 0, 100, 100 ] * => [ 0, 0, 100, 100 ] * * gravity.geometry = { x: 0, y: 0, width: 100, height: 100 } * => { x: 0, y: 0, width: 100, height: 100 } * * gravity.geometry = "0x0+100+100" * => "0x0+100+100" * * gravity.geometry = Subtlext::Geometry(0, 0, 100, 100) * => # */ VALUE subGravityGeometryWriter(int argc, VALUE *argv, VALUE self) { VALUE klass = Qnil, geom = Qnil; /* Check ruby object */ rb_check_frozen(self); subSubtlextConnect(NULL); ///< Implicit open connection /* Delegate arguments */ klass = rb_const_get(mod, rb_intern("Geometry")); geom = rb_funcall2(klass, rb_intern("new"), argc, argv); /* Update geometry */ if(RTEST(geom)) rb_iv_set(self, "@geometry", geom); return geom; } /* }}} */ /* subGravityTilingWriter {{{ */ /* * call-seq: tiling=(value) -> Symbol or nil * * Set the tiling mode for gravity * * gravity.tiling = :vert * => :vert * * gravity.tiling = :horz * => :horz * * gravity.tiling = nil * => nil */ VALUE subGravityTilingWriter(VALUE self, VALUE value) { int flags = 0; VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); /* Check value type */ switch(rb_type(value)) { case T_SYMBOL: if(CHAR2SYM("horz") == value) flags = SUB_EWMH_HORZ; else if(CHAR2SYM("vert") == value) flags = SUB_EWMH_VERT; break; case T_NIL: break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } /* Assemble message */ data.l[0] = FIX2INT(id); data.l[1] = flags; subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_GRAVITY_FLAGS", data, 32, True); return value; } /* }}} */ /* subGravityToString {{{ */ /* * call-seq: to_str -> String * * Convert this Gravity object to string. * * puts gravity * => "TopLeft" */ VALUE subGravityToString(VALUE self) { VALUE name = Qnil; /* Check ruby object */ GET_ATTR(self, "@name", name); return name; } /* }}} */ /* subGravityToSym {{{ */ /* * call-seq: to_sym -> Symbol * * Convert this Gravity object to symbol. * * puts gravity.to_sym * => :center */ VALUE subGravityToSym(VALUE self) { VALUE name = Qnil; /* Check ruby object */ GET_ATTR(self, "@name", name); return CHAR2SYM(RSTRING_PTR(name)); } /* }}} */ /* subGravityKill {{{ */ /* * call-seq: kill -> nil * * Remove this Gravity from subtle and freeze this object. * * gravity.kill * => nil */ VALUE subGravityKill(VALUE self) { VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = FIX2INT(id); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_GRAVITY_KILL", data, 32, True); rb_obj_freeze(self); ///< Freeze object return Qnil; } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtlext/screen.c0000644000175000017500000002604211770332063020302 0ustar formorerformorer /** * @package subtle * * @file subtle ruby extension * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtlext/screen.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtlext.h" /* ScreenList {{{ */ VALUE ScreenList(void) { unsigned long nworkareas = 0; VALUE method = Qnil, klass = Qnil, array = Qnil, screen = Qnil, geom = Qnil; long *workareas = NULL; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ method = rb_intern("new"); klass = rb_const_get(mod, rb_intern("Screen")); array = rb_ary_new(); /* Get workarea list */ if((workareas = (long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "_NET_WORKAREA", False), &nworkareas))) { int i; for(i = 0; i < nworkareas / 4; i++) { /* Create new screen */ screen = rb_funcall(klass, method, 1, INT2FIX(i)); geom = subGeometryInstantiate(workareas[i * 4 + 0], workareas[i * 4 + 1], workareas[i * 4 + 2], workareas[i * 4 + 3]); rb_iv_set(screen, "@geometry", geom); rb_ary_push(array, screen); } free(workareas); } return array; } /* }}} */ /* Singleton */ /* subScreenSingFind {{{ */ /* * call-seq: find(value) -> Subtlext::Screen or nil * [value] -> Subtlext::Screen or nil * * Find Screen by a given value which can be of following type: * * [fixnum] Array id * [Subtlext::Geometry] Geometry * * Subtlext::Screen.find(1) * => # * * Subtlext::Screen[1] * => # * * Subtlext::Screen.find(Subtlext::Geometry(10, 10, 100, 100) * => # */ VALUE subScreenSingFind(VALUE self, VALUE value) { VALUE screen = Qnil; /* Check object type */ switch(rb_type(value)) { case T_FIXNUM: { VALUE screens = ScreenList(); screen = rb_ary_entry(screens, FIX2INT(value)); } break; case T_OBJECT: { VALUE klass = rb_const_get(mod, rb_intern("Geometry")); /* Check object instance */ if(rb_obj_is_instance_of(value, klass)) { unsigned long nworkareas = 0; long *workareas = NULL; subSubtlextConnect(NULL); ///< Implicit open connection /* Get workarea list */ if((workareas = (long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "_NET_WORKAREA", False), &nworkareas))) { int i; XRectangle geom = { 0 }; subGeometryToRect(value, &geom); for(i = 0; i < nworkareas / 4; i++) { /* Check if coordinates are in screen rects */ if(geom.x >= workareas[i * 4 + 0] && geom.x < workareas[i * 4 + 0] + workareas[i * 4 + 2] && geom.y >= workareas[i * 4 + 1] && geom.y < workareas[i * 4 + 1] + workareas[i * 4 + 3]) { VALUE geometry = Qnil; /* Create new screen */ screen = subScreenInstantiate(i); geometry = subGeometryInstantiate( workareas[i * 4 + 0], workareas[i * 4 + 1], workareas[i * 4 + 2], workareas[i * 4 + 3]); rb_iv_set(screen, "@geometry", geometry); break; } } free(workareas); } } } break; default: rb_raise(rb_eArgError, "Unexpected value type `%s'", rb_obj_classname(value)); } return screen; } /* }}} */ /* subScreenSingList {{{ */ /* * call-seq: list -> Array * * Get Array of all Screen * * Subtlext::Screen.list * => [#, #] * * Subtlext::Screen.list * => [] */ VALUE subScreenSingList(VALUE self) { return ScreenList(); } /* }}} */ /* subScreenSingCurrent {{{ */ /* * call-seq: current -> Subtlext::Screen * * Get current active Screen * * Subtlext::Screen.current * => # */ VALUE subScreenSingCurrent(VALUE self) { int rx = 0, ry = 0, x = 0, y = 0; unsigned int mask = 0; unsigned long nworkareas = 0, npanels = 0; long *workareas = NULL, *panels = NULL; VALUE screen = Qnil; Window root = None, win = None; subSubtlextConnect(NULL); ///< Implicit open connection /* Get current screen */ XQueryPointer(display, DefaultRootWindow(display), &root, &win, &rx, &ry, &x, &y, &mask); /* Fetch data */ workareas = (long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "_NET_WORKAREA", False), &nworkareas); panels = (long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_SCREEN_PANELS", False), &npanels); /* Get workarea list */ if(workareas && panels) { int i; for(i = 0; i < nworkareas / 4; i++) { /* Check if coordinates are in screen rects including panel size */ if(rx >= workareas[i * 4 + 0] && rx < workareas[i * 4 + 0] + workareas[i * 4 + 2] && ry >= (workareas[i * 4 + 1] - panels[i * 2 + 0]) && ry < (workareas[i * 4 + 1] + workareas[i * 4 + 3] + panels[i * 2 + 1])) { VALUE geometry = Qnil; /* Create new screen */ screen = subScreenInstantiate(i); geometry = subGeometryInstantiate(workareas[i * 4 + 0], workareas[i * 4 + 1], workareas[i * 4 + 2], workareas[i * 4 + 3]); rb_iv_set(screen, "@geometry", geometry); } } } if(workareas) free(workareas); if(panels) free(panels); return screen; } /* }}} */ /* Helper */ /* subScreenInstantiate {{{ */ VALUE subScreenInstantiate(int id) { VALUE klass = Qnil, screen = Qnil; /* Create new instance */ klass = rb_const_get(mod, rb_intern("Screen")); screen = rb_funcall(klass, rb_intern("new"), 1, INT2FIX(id)); return screen; } /* }}} */ /* Class */ /* subScreenInit {{{ */ /* * call-seq: new(id) -> Subtlext::Screen * * Create a new Screen object * * screen = Subtlext::Screen.new(0) * => # */ VALUE subScreenInit(VALUE self, VALUE id) { if(!FIXNUM_P(id) || 0 > FIX2INT(id)) rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(id)); /* Init object */ rb_iv_set(self, "@id", id); rb_iv_set(self, "@geometry", Qnil); subSubtlextConnect(NULL); ///< Implicit open connection return self; } /* }}} */ /* subScreenUpdate {{{ */ /* * call-seq: update -> Subtlext::Screen * * Update Screen properties * * screen.update * => # */ VALUE subScreenUpdate(VALUE self) { VALUE id = Qnil, screens = Qnil, screen = Qnil; /* Check ruby object */ GET_ATTR(self, "@id", id); /* Find screen */ if((screens = ScreenList()) && RTEST(screen = rb_ary_entry(screens, FIX2INT(id)))) { VALUE geometry = rb_iv_get(screen, "@geometry"); rb_iv_set(self, "@geometry", geometry); } else rb_raise(rb_eStandardError, "Invalid screen id `%d'", (int)FIX2INT(id)); return self; } /* }}} */ /* subScreenJump {{{ */ /* * call-seq: screen -> Subtlext::Screen * * Jump to this Screen * * screen.jump * => # */ VALUE subScreenJump(VALUE self) { VALUE id = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@id", id); subSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = FIX2INT(id); subSharedMessage(display, DefaultRootWindow(display), "SUBTLE_SCREEN_JUMP", data, 32, True); return self; } /* }}} */ /* subScreenViewReader {{{ */ /* * call-seq: view -> Subtlext::View * * Get active view for screen * * screen.view * => # */ VALUE subScreenViewReader(VALUE self) { VALUE ret = Qnil; int nnames = 0; char **names = NULL; unsigned long *screens = NULL; subSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ names = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames); screens = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_SCREEN_VIEWS", False), NULL); /* Check results */ if(names && screens) { int id = 0, vid = 0; if(0 <= (id = FIX2INT(rb_iv_get(self, "@id")))) { if(0 <= (vid = screens[id]) && vid < nnames) { ret = subViewInstantiate(names[vid]); if(!NIL_P(ret)) rb_iv_set(ret, "@id", INT2FIX(vid)); } } } if(names) XFreeStringList(names); if(screens) free(screens); return ret; } /* }}} */ /* subScreenViewWriter {{{ */ /* * call-seq: view=(fixnum) -> Fixnum * view=(symbol) -> Symbol * view=(object) -> Subtlext::View * * Set active view for screen * * screen.view = :www * => nil * * screen.view = Subtlext::View[0] * => nil */ VALUE subScreenViewWriter(VALUE self, VALUE value) { VALUE vid = Qnil, view = Qnil, sid = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ GET_ATTR(self, "@id", sid); subSubtlextConnect(NULL); ///< Implicit open connection /* Check instance type */ if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("View")))) view = value; else view = subViewSingFirst(Qnil, value); GET_ATTR(view, "@id", vid); /* Send message */ data.l[0] = FIX2LONG(vid); data.l[1] = CurrentTime; data.l[2] = FIX2LONG(sid); subSharedMessage(display, DefaultRootWindow(display), "_NET_CURRENT_DESKTOP", data, 32, True); return value; } /* }}} */ /* subScreenAskCurrent {{{ */ /* * call-seq: screen? -> true or false * * Check if this Screen is the current active Screen * * screen.current? * => true * * screen.current? * => false */ VALUE subScreenAskCurrent(VALUE self) { /* Check ruby object */ rb_check_frozen(self); return rb_equal(self, subScreenSingCurrent(Qnil)); } /* }}} */ /* subScreenToString {{{ */ /* * call-seq: to_str -> String * * Convert Screen object to String * * puts screen * => "0x0+800+600" */ VALUE subScreenToString(VALUE self) { VALUE geom = Qnil; /* Check ruby object */ GET_ATTR(self, "@geometry", geom); return subGeometryToString(geom); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/0000755000175000017500000000000011770527221016301 5ustar formorerformorersubtle-0.11.3224-xi/src/subtle/client.c0000644000175000017500000014745111770332063017735 0ustar formorerformorer /** * @package subtle * * @file Client functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/client.c,v 3209 2012/05/22 23:44:10 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include "subtle.h" /* Flags {{{ */ #define EDGE_LEFT (1L << 0) #define EDGE_RIGHT (1L << 1) #define EDGE_TOP (1L << 2) #define EDGE_BOTTOM (1L << 3) /* }}} */ /* Typedef {{{ */ typedef struct clientmwmhints_t { unsigned long flags; unsigned long functions; unsigned long decorations; long input_mode; unsigned long status; } ClientMWMHints; /* }}} */ /* Private */ /* ClientMask {{{ */ static void ClientMask(XRectangle *geom) { XDrawRectangle(subtle->dpy, ROOT, subtle->gcs.invert, geom->x - 1, geom->y - 1, geom->width + 1, geom->height + 1); } /* }}} */ /* ClientGravity {{{ */ int ClientGravity(void) { int grav = 0; SubClient *c = NULL; /* Default gravity */ if(-1 == subtle->gravity) { /* Copy gravity from current client */ if((c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID)))) grav = c->gravityid; } else grav = subtle->gravity; ///< Set default return grav; } /* }}} */ /* ClientBounds {{{ */ static void ClientBounds(SubClient *c, XRectangle *bounds, XRectangle *geom, int adjustx, int adjusty) { DEAD(c); assert(c && geom); /* Check size hints */ if(!(c->flags & SUB_CLIENT_MODE_FIXED) && (subtle->flags & SUB_SUBTLE_RESIZE || c->flags & (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_RESIZE))) { int bw = 0, maxw = 0, maxh = 0, diffw = 0, diffh = 0; /* Calculate max width and max height for bounds */ bw = 2 * BORDER(c) + subtle->styles.clients.margin.left + subtle->styles.clients.margin.right; maxw = -1 == c->maxw ? bounds->width - bw : c->maxw; maxh = -1 == c->maxh ? bounds->height - bw : c->maxh; /* Limit width and height */ if(geom->width < c->minw) geom->width = c->minw; if(geom->width > maxw) geom->width = maxw; if(geom->height < c->minh) geom->height = c->minh; if(geom->height > maxh) geom->height = maxh; /* Adjust based on increment values (see ICCCM 4.1.2.3) */ diffw = (geom->width - c->basew) % c->incw; diffh = (geom->height - c->baseh) % c->inch; /* Adjust x and/or y */ if(adjustx) geom->x += diffw; if(adjusty) geom->y += diffh; /* Center client on current gravity */ if(!(c->flags & SUB_CLIENT_MODE_FLOAT)) { geom->x += 0 < diffw ? diffw / 2 : 0; geom->y += 0 < diffh ? diffh / 2 : 0; } geom->width -= diffw; geom->height -= diffh; /* Check aspect ratios */ if(c->minr && geom->height * c->minr > geom->width) geom->width = (int)(geom->height * c->minr); if(c->maxr && geom->height * c->maxr < geom->width) geom->width = (int)(geom->height * c->maxr); } } /* }}} */ /* ClientSnap {{{ */ static void ClientSnap(SubClient *c, SubScreen *s, XRectangle *geom) { DEAD(c); assert(c && s && geom); /* Snap to screen border when value is in snap margin - X axis */ if(abs(s->geom.x - geom->x) <= subtle->snap) geom->x = s->geom.x + BORDER(c); else if(abs((s->geom.x + s->geom.width) - (geom->x + geom->width + BORDER(c))) <= subtle->snap) { geom->x = s->geom.x + s->geom.width - geom->width - BORDER(c); } /* Snap to screen border when is in snap margin - Y axis */ if(abs(s->geom.y - geom->y) <= subtle->snap) geom->y = s->geom.y + BORDER(c); else if(abs((s->geom.y + s->geom.height) - (geom->y + geom->height + BORDER(c))) <= subtle->snap) { geom->y = s->geom.y + s->geom.height - geom->height - BORDER(c); } } /* }}} */ /* ClientResize {{{ */ static void ClientResize(SubClient *c, XRectangle *bounds) { assert(c); /* Update border and gap */ c->geom.x += subtle->styles.clients.margin.left; c->geom.y += subtle->styles.clients.margin.top; c->geom.width -= (2 * BORDER(c) + subtle->styles.clients.margin.left + subtle->styles.clients.margin.right); c->geom.height -= (2 * BORDER(c) + subtle->styles.clients.margin.top + subtle->styles.clients.margin.bottom); subClientResize(c, bounds, True); XMoveResizeWindow(subtle->dpy, c->win, c->geom.x, c->geom.y, c->geom.width, c->geom.height); } /* }}} */ /* ClientTile {{{ */ static void ClientTile(int gravity, int screen) { int i, used = 0, pos = 0, calc = 0, fix = 0; XRectangle geom = { 1 }; SubScreen *s = SCREEN(subArrayGet(subtle->screens, screen)); SubGravity *g = GRAVITY(subArrayGet(subtle->gravities, gravity)); /* Pass 1: Count clients with this gravity */ for(i = 0; i < subtle->clients->ndata; i++) { SubClient *c = CLIENT(subtle->clients->data[i]); if(c->gravityid == gravity && c->screenid == screen && subtle->visible_tags & c->tags && !(c->flags &(SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_FULL))) used++; } if(0 == used || !s || !g) return; /* Calculate tiled gravity value and rounding fix */ subGravityGeometry(g, &(s->geom), &geom); if(g->flags & SUB_GRAVITY_HORZ) { calc = geom.width / used; fix = geom.width - calc * used; } else { calc = geom.height / used; fix = geom.height - calc * used; } /* Pass 2: Update geometry of every client with this gravity */ for(i = 0; i < subtle->clients->ndata; i++) { SubClient *c = CLIENT(subtle->clients->data[i]); if(c->gravityid == gravity && c->screenid == screen && subtle->visible_tags & c->tags && !(c->flags & (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_FULL))) { if(g->flags & SUB_GRAVITY_HORZ) { c->geom.width = pos == used ? calc + fix : calc; c->geom.height = geom.height; c->geom.x = geom.x + pos++ * calc; c->geom.y = geom.y; } else { c->geom.width = geom.width; c->geom.height = pos == used ? calc + fix : calc; c->geom.x = geom.x; c->geom.y = geom.y + pos++ * calc; } ClientResize(c, &(s->geom)); } } } /* }}} */ /* ClientZaphod {{{ */ static void ClientZaphod(SubClient *c, XRectangle *bounds) { int i, flags = (SUB_SCREEN_PANEL1|SUB_SCREEN_PANEL2); /* Update bounds according to styles */ bounds->x = subtle->styles.subtle.padding.left; bounds->y = subtle->styles.subtle.padding.top; bounds->width = subtle->width - subtle->styles.subtle.padding.left - subtle->styles.subtle.padding.right; bounds->height = subtle->height - subtle->styles.subtle.padding.top - subtle->styles.subtle.padding.bottom; /* Iterate over screens to find fitting square */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s2 = SCREEN(subtle->screens->data[i]); if(s2->flags & flags) { if(s2->flags & SUB_SCREEN_PANEL1) { bounds->y += subtle->ph; bounds->height -= subtle->ph; } if(s2->flags & SUB_SCREEN_PANEL2) bounds->height -= subtle->ph; flags &= ~(s2->flags & (SUB_SCREEN_PANEL1|SUB_SCREEN_PANEL2)); } } } /* }}} */ /* ClientCompare {{{ */ static int ClientCompare(const void *a, const void *b) { int ret = 0, dirret = 0; SubClient *c1 = *(SubClient **)a, *c2 = *(SubClient **)b; assert(a && b); /* Direction is required when we change stacking on a level */ if(SUB_CLIENT_RESTACK_DOWN == c1->dir) dirret = -1; else if(SUB_CLIENT_RESTACK_UP == c1->dir) dirret = 1; else if(SUB_CLIENT_RESTACK_DOWN == c2->dir) dirret = 1; else if(SUB_CLIENT_RESTACK_UP == c2->dir) dirret = -1; /* Complicated comparisons to ensure stacking order. Our desired * order is following: desktop < gravity < float < full * * This function returns following values: * -1 => c1 is on a lower level * 0 => c1 and c2 are on the same level * 1 => c1 is on a higher level */ if(c1->flags & SUB_CLIENT_TYPE_DESKTOP) { if(c2->flags & SUB_CLIENT_TYPE_DESKTOP) ret = dirret; else ret = 0; } else if(c1->flags & SUB_CLIENT_MODE_FULL) { if(c2->flags & SUB_CLIENT_MODE_FULL) ret = dirret; else ret = 1; } else if(c1->flags & SUB_CLIENT_MODE_FLOAT) { if(c2->flags & SUB_CLIENT_MODE_FULL) ret = -1; else if(c2->flags & SUB_CLIENT_MODE_FLOAT) ret = dirret; else ret = 1; } else { if(c2->flags & SUB_CLIENT_TYPE_DESKTOP) ret = 1; else if(c2->flags & (SUB_CLIENT_MODE_FLOAT| SUB_CLIENT_MODE_FULL)) ret = -1; else ret = dirret; } return ret; } /* }}} */ /* Public */ /** subClientNew {{{ * @brief Create new client * @param[in] win Client window * @return Returns a new #SubClient or \p NULL **/ SubClient * subClientNew(Window win) { int i, grav = 0, flags = 0; long vid = 0, extents[4] = { 0 }; XWindowAttributes attrs; XSetWindowAttributes sattrs; Window *leader = NULL; SubClient *c = NULL; assert(win); /* Check override_redirect */ XGetWindowAttributes(subtle->dpy, win, &attrs); if(True == attrs.override_redirect) return NULL; /* Create new client */ c = CLIENT(subSharedMemoryAlloc(1, sizeof(SubClient))); c->gravities = (int *)subSharedMemoryAlloc(subtle->views->ndata, sizeof(int)); c->flags = (SUB_TYPE_CLIENT|SUB_CLIENT_INPUT); c->gravityid = -1; ///< Force update c->dir = -1; c->win = win; /* Window attributes */ c->cmap = attrs.colormap; c->geom.x = attrs.x; c->geom.y = attrs.y; c->geom.width = MAX(MINW, attrs.width); c->geom.height = MAX(MINH, attrs.height); /* Init gravities */ grav = ClientGravity(); for(i = 0; i < subtle->views->ndata; i++) c->gravities[i] = grav; /* Fetch name, instance, class and role */ subSharedPropertyClass(subtle->dpy, c->win, &c->instance, &c->klass); subSharedPropertyName(subtle->dpy, c->win, &c->name, c->klass); c->role = subSharedPropertyGet(subtle->dpy, c->win, XA_STRING, subEwmhGet(SUB_EWMH_WM_WINDOW_ROLE), NULL); /* X properties */ sattrs.border_pixel = subtle->styles.clients.bg; ///< Inactive sattrs.event_mask = CLIENTMASK; XChangeWindowAttributes(subtle->dpy, c->win, CWBorderPixel|CWEventMask, &sattrs); XAddToSaveSet(subtle->dpy, c->win); XSaveContext(subtle->dpy, c->win, CLIENTID, (void *)c); XSetWindowBorderWidth(subtle->dpy, c->win, subtle->styles.clients.border.top); /* Update client */ subEwmhSetWMState(c->win, WithdrawnState); subClientSetProtocols(c); subClientSetStrut(c); subClientSetType(c, &flags); subClientRetag(c, &flags); subClientSetSizeHints(c, &flags); subClientSetWMHints(c, &flags); subClientSetState(c, &flags); subClientSetTransient(c, &flags); subClientSetMWMHints(c); subClientToggle(c, flags, False); subGrabUnset(c->win); /* Set leader window */ if((leader = (Window *)subSharedPropertyGet(subtle->dpy, c->win, XA_WINDOW, subEwmhGet(SUB_EWMH_WM_CLIENT_LEADER), NULL))) { c->leader = *leader; free(leader); } /* EWMH: Gravity, screen, desktop, extents */ subEwmhSetCardinals(c->win, SUB_EWMH_SUBTLE_CLIENT_GRAVITY, (long *)&subtle->gravity, 1); subEwmhSetCardinals(c->win, SUB_EWMH_SUBTLE_CLIENT_SCREEN, (long *)&c->screenid, 1); subEwmhSetCardinals(c->win, SUB_EWMH_NET_WM_DESKTOP, &vid, 1); subEwmhSetCardinals(c->win, SUB_EWMH_NET_FRAME_EXTENTS, extents, 4); subSubtleLogDebugSubtle("New: name=%s, instance=%s, " "class=%s, win=%#lx, input=%d, focus=%d\n", c->name, c->instance, c->klass, win, !!(c->flags & SUB_CLIENT_INPUT), !!(c->flags & SUB_CLIENT_FOCUS)); return c; } /* }}} */ /** subClientConfigure {{{ * @brief Send a configure request to client * @param[in] c A #SubClient **/ void subClientConfigure(SubClient *c) { XConfigureEvent ev; assert(c); DEAD(c); /* Assemble event */ ev.type = ConfigureNotify; ev.event = c->win; ev.window = c->win; ev.x = c->geom.x; ev.y = c->geom.y; ev.width = c->geom.width; ev.height = c->geom.height; ev.border_width = subtle->styles.clients.border.top; ev.above = None; ev.override_redirect = False; XSendEvent(subtle->dpy, c->win, False, StructureNotifyMask, (XEvent *)&ev); subSubtleLogDebugSubtle("Configure: win=%#lx " "x=%03d, y=%03d, width=%03d, height=%03d\n", c->win, c->geom.x, c->geom.y, c->geom.width, c->geom.height); } /* }}} */ /** subClientDimension {{{ * @brief Redimension clients * @param[in] id View id **/ void subClientDimension(int id) { int i, j; /* Update all clients */ for(i = 0; i < subtle->clients->ndata; i++) { SubClient *c = CLIENT(subtle->clients->data[i]); /* Shift if necessary */ for(j = id; -1 < id && j < subtle->views->ndata; j++) c->gravities[j] = c->gravities[j + 1]; /* Resize array */ c->gravities = (int *)subSharedMemoryRealloc((void *)c->gravities, subtle->views->ndata * sizeof(int)); if(-1 == id) ///< Initialize c->gravities[subtle->views->ndata - 1] = ClientGravity(); } } /* }}} */ /** subClientFocus {{{ * @brief Set focus to client * @param[in] c A #SubClient * @param[in] warp Whether to warp pointer to window **/ void subClientFocus(SubClient *c, int warp) { SubScreen *s = NULL; SubView *v = NULL; SubClient *focus = NULL; DEAD(c); assert(c); if(!VISIBLE(c)) return; /* Remove urgent after getting focus */ if(c->flags & SUB_CLIENT_MODE_URGENT) { c->flags &= ~SUB_CLIENT_MODE_URGENT; subtle->urgent_tags &= ~c->tags; } /* Unset current focus */ if((focus = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID)))) { int i; subGrabUnset(focus->win); /* Reorder focus history */ for(i = (HISTORYSIZE - 1); 0 < i; i--) subtle->windows.focus[i] = subtle->windows.focus[i - 1]; subtle->windows.focus[0] = 0; /* Exclude desktop type windows */ if(!(focus->flags & SUB_CLIENT_TYPE_DESKTOP)) XSetWindowBorder(subtle->dpy, focus->win, subtle->styles.clients.bg); } /* Check client input focus type (see ICCCM 4.1.7, 4.1.2.7, 4.2.8) */ if(!(c->flags & SUB_CLIENT_INPUT) && c->flags & SUB_CLIENT_FOCUS) { subEwmhMessage(c->win, SUB_EWMH_WM_PROTOCOLS, NoEventMask, subEwmhGet(SUB_EWMH_WM_TAKE_FOCUS), CurrentTime, 0, 0, 0); } else if(c->flags & SUB_CLIENT_INPUT) XSetInputFocus(subtle->dpy, c->win, RevertToPointerRoot, CurrentTime); /* Update focus */ subtle->windows.focus[0] = c->win; subGrabSet(c->win, SUB_GRAB_MOUSE); /* Exclude desktop and dock type windows */ if(!(c->flags & (SUB_CLIENT_TYPE_DESKTOP|SUB_CLIENT_TYPE_DOCK))) XSetWindowBorder(subtle->dpy, c->win, subtle->styles.clients.fg); /* EWMH: Active window */ subEwmhSetWindows(ROOT, SUB_EWMH_NET_ACTIVE_WINDOW, subtle->windows.focus, HISTORYSIZE); /* EWMH: Current desktop */ if((s = SCREEN(subArrayGet(subtle->screens, c->screenid)))) { subEwmhSetCardinals(ROOT, SUB_EWMH_NET_CURRENT_DESKTOP, (long *)&s->viewid, 1); } /* Set view focus */ if((v = VIEW(subArrayGet(subtle->views, s->viewid)))) v->focus = c->win;; /* Hook: Focus */ subHookCall((SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_FOCUS), (void *)c); /* Warp pointer */ if(warp && !(subtle->flags & SUB_SUBTLE_SKIP_WARP)) subClientWarp(c); /* Update screen */ subScreenUpdate(); subScreenRender(); } /* }}} */ /** subClientNext {{{ * @brief Find next client and set focus to it * @param[in] screenid Screen id * @param[in] jump Jump to other screen * @return Returns a new #SubClient or \p NULL **/ SubClient * subClientNext(int screenid, int jump) { int i; SubClient *c = NULL; /* Pass 1: Check focus history of current screen */ for(i = 1; i < HISTORYSIZE; i++) { if((c = CLIENT(subSubtleFind(subtle->windows.focus[i], CLIENTID)))) { /* Check visibility on current screen */ if(c->screenid == screenid && ALIVE(c) && VISIBLE(c) && c->win != subtle->windows.focus[0]) return c; } } /* Pass 2: Check client stacking list backwards of current screen */ for(i = subtle->clients->ndata - 1; 0 <= i; i--) { c = CLIENT(subtle->clients->data[i]); /* Check visibility on current screen */ if(c->screenid == screenid && ALIVE(c) && VISIBLE(c) && c->win != subtle->windows.focus[0]) return c; } /* Pass 3: Check client stacking list backwards of any visible screen */ if(1 < subtle->screens->ndata && jump) { for(i = subtle->clients->ndata - 1; 0 <= i; i--) { c = CLIENT(subtle->clients->data[i]); /* Check visibility on current screen */ if(ALIVE(c) && VISIBLE(c) && c->win != subtle->windows.focus[0]) return c; } } return NULL; } /* }}} */ /** subClientWarp {{{ * @brief Warp pointer to window center * @param[in] c A #SubClient **/ void subClientWarp(SubClient *c) { DEAD(c); assert(c); /* Move pointer to window center */ XWarpPointer(subtle->dpy, None, ROOT, 0, 0, 0, 0, c->geom.x + c->geom.width / 2, c->geom.y + c->geom.height / 2); subSubtleLogDebugSubtle("Warp\n"); } /* }}} */ /** subClientDrag {{{ * @brief Move and/or drag client * @param[in] c A #SubClient * @param[in] mode Drag/move mode * @param[in] direction Resize/drag direction **/ void subClientDrag(SubClient *c, int mode, int direction) { XEvent ev; Window root = None, win = None; unsigned int mask = 0; int loop = True, edge = 0, fx = 0, fy = 0, dx = 0, dy = 0; int wx = 0, wy = 0, ww = 0, wh = 0, rx = 0, ry = 0; SubScreen *s = NULL; XRectangle geom = { 0 }; Cursor cursor; DEAD(c); assert(c); /* Init {{{ */ XQueryPointer(subtle->dpy, c->win, &root, &win, &rx, &ry, &wx, &wy, &mask); geom.x = rx - wx; geom.y = ry - wy; geom.width = ww = c->geom.width; geom.height = wh = c->geom.height; /* Set max width/height */ s = SCREEN(subtle->screens->data[c->screenid]); /* Set variables according to mode */ switch(mode) { case SUB_DRAG_MOVE: cursor = subtle->cursors.move; break; case SUB_DRAG_RESIZE: cursor = subtle->cursors.resize; /* Select starting edge */ edge |= (wx < (geom.width / 2)) ? EDGE_LEFT : EDGE_RIGHT; edge |= (wy < (geom.height / 2)) ? EDGE_TOP : EDGE_BOTTOM; /* Set starting point */ if(edge & EDGE_LEFT) { fx = geom.x + geom.width; dx = rx - c->geom.x; } else if(edge & EDGE_RIGHT) { fx = geom.x; dx = geom.x + geom.width - rx; } if(edge & EDGE_TOP) { fy = geom.y + geom.height; dy = ry - c->geom.y; } else if(edge & EDGE_BOTTOM) { fy = geom.y; dy = geom.y + geom.height - ry; } break; } /* }}} */ /* Grab pointer server */ XGrabPointer(subtle->dpy, c->win, True, GRABMASK, GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime); XGrabServer(subtle->dpy); switch(direction) { case SUB_GRAB_DIRECTION_UP: /* {{{ */ if(SUB_DRAG_RESIZE == mode) { c->geom.y -= c->inch; c->geom.height += c->inch; } else c->geom.y -= subtle->step; ClientSnap(c, s, &c->geom); ClientBounds(c, &(s->geom), &c->geom, False, False); break; /* }}} */ case SUB_GRAB_DIRECTION_RIGHT: /* {{{ */ if(SUB_DRAG_RESIZE == mode) c->geom.width += c->incw; else c->geom.x += subtle->step; ClientSnap(c, s, &c->geom); ClientBounds(c, &(s->geom), &c->geom, False, False); break; /* }}} */ case SUB_GRAB_DIRECTION_DOWN: /* {{{ */ if(SUB_DRAG_RESIZE == mode) c->geom.height += c->inch; else c->geom.y += subtle->step; ClientSnap(c, s, &c->geom); ClientBounds(c, &(s->geom), &c->geom, False, False); break; /* }}} */ case SUB_GRAB_DIRECTION_LEFT: /* {{{ */ if(SUB_DRAG_RESIZE == mode) { c->geom.x -= c->incw; c->geom.width += c->incw; } else c->geom.x -= subtle->step; ClientSnap(c, s, &c->geom); ClientBounds(c, &(s->geom), &c->geom, False, False); break; /* }}}*/ default: /* {{{ */ ClientMask(&geom); /* Start event loop */ while(loop) { XMaskEvent(subtle->dpy, DRAGMASK, &ev); switch(ev.type) { case EnterNotify: win = ev.xcrossing.window; break; ///< Find destination window case ButtonRelease: loop = False; break; case FocusIn: case FocusOut: break; ///< Ignore focus changes case MotionNotify: /* {{{ */ if(mode & (SUB_DRAG_MOVE|SUB_DRAG_RESIZE)) { /* Check values */ if(!XYINRECT(ev.xmotion.x_root - dx, ev.xmotion.y_root - dy, s->geom)) continue; ClientMask(&geom); /* Calculate selection rect */ switch(mode) { case SUB_DRAG_MOVE: /* {{{ */ geom.x = (rx - wx) - (rx - ev.xmotion.x_root); geom.y = (ry - wy) - (ry - ev.xmotion.y_root); ClientSnap(c, s, &geom); break; /* }}} */ case SUB_DRAG_RESIZE: /* {{{ */ /* Handle resize based on edge */ if(edge & EDGE_LEFT) { geom.x = ev.xmotion.x_root - dx; geom.width = fx - ev.xmotion.x_root + dx; } else if(edge & EDGE_RIGHT) { geom.x = fx; geom.width = ev.xmotion.x_root - fx + dx; } if(edge & EDGE_TOP) { geom.y = ev.xmotion.y_root - dy; geom.height = fy - ev.xmotion.y_root + dy; } else if(edge & EDGE_BOTTOM) { geom.y = fy; geom.height = ev.xmotion.y_root - fy + dy; } /* Adjust bounds based on edge */ ClientBounds(c, &(s->geom), &geom, (edge & EDGE_LEFT), (edge & EDGE_TOP)); break; /* }}} */ } ClientMask(&geom); } break; /* }}} */ } } ClientMask(&geom); ///< Erase mask /* Subtract border width */ if(!(c->flags & SUB_CLIENT_MODE_BORDERLESS)) { geom.x -= subtle->styles.clients.border.top; geom.y -= subtle->styles.clients.border.top; } /* }}} */ c->geom = geom; } XMoveResizeWindow(subtle->dpy, c->win, c->geom.x, c->geom.y, c->geom.width, c->geom.height); /* Remove grabs */ XUngrabPointer(subtle->dpy, CurrentTime); XUngrabServer(subtle->dpy); } /* }}} */ /** subClientTag {{{ * @brief Set tag properties to client * @param[in] c A #SubClient * @param[in] tag Tag id * @param[inout] flags Mode flags * @return Return changed flags **/ void subClientTag(SubClient *c, int tag, int *flags) { SubTag *t = NULL; DEAD(c); assert(c); /* Update flags and tags */ if((t = TAG(subArrayGet(subtle->tags, tag)))) { int i; /* Collect flags and tags */ *flags |= (t->flags & (TYPES_ALL|MODES_ALL)); c->tags |= (1L << (tag + 1)); /* Set size/position and enable float */ if(t->flags & (SUB_TAG_GEOMETRY|SUB_TAG_POSITION)) { *flags |= SUB_CLIENT_MODE_FLOAT; ///< Disable size checks /* Apply size/position */ if(t->flags & SUB_TAG_GEOMETRY) c->geom = t->geom; else { c->geom.x = t->geom.x; c->geom.y = t->geom.y; } } /* Set screen */ if(t->flags & SUB_CLIENT_MODE_STICK && -1 != t->screenid) { c->flags |= SUB_CLIENT_MODE_STICK_SCREEN; c->screenid = t->screenid; } /* Set gravity matching views */ for(i = 0; i < subtle->views->ndata; i++) { SubView *v = VIEW(subtle->views->data[i]); /* Match views with this tag or sticky only */ if(v->tags & (1L << (tag + 1)) || t->flags & SUB_CLIENT_MODE_STICK) if(t->flags & SUB_TAG_GRAVITY) c->gravities[i] = t->gravityid; } /* Call proc if any */ if(t->flags & SUB_TAG_PROC) subRubyCall(SUB_CALL_HOOKS, t->proc, (void *)c); } } /* }}} */ /** subClientRetag {{{ * @brief Set client tags * @param[in] c A #SubClient * @param[inout] flags Mode flags **/ void subClientRetag(SubClient *c, int *flags) { int i; DEAD(c); assert(c); c->tags = 0; ///< Reset tags /* Check matching tags */ for(i = 0; i < subtle->tags->ndata; i++) { /* Check if tag matches client */ if(subTagMatcherCheck(TAG(subtle->tags->data[i]), c)) subClientTag(c, i, flags); } /* Check if client is visible on at least one screen w/o stick */ if(!(c->flags & SUB_CLIENT_MODE_STICK) && !(*flags & SUB_CLIENT_MODE_STICK)) { int visible = 0; for(i = 0; i < subtle->views->ndata; i++) { if(VIEW(subtle->views->data[i])->tags & c->tags) { visible++; break; } } if(0 == visible) subClientTag(c, 0, flags); ///< Set default tag } /* EWMH: Tags */ subEwmhSetCardinals(c->win, SUB_EWMH_SUBTLE_CLIENT_TAGS, (long *)&c->tags, 1); } /* }}} */ /** subClientResize {{{ * @brief Resize client for screen * @param[in] c A #SubClient * @param[in] bounds A #XRectangle * @param[in] size_hints Apply size hints **/ void subClientResize(SubClient *c, XRectangle *bounds, int size_hints) { DEAD(c); assert(c); /* Honor size hints */ if(size_hints) ClientBounds(c, bounds, &c->geom, False, False); /* Fit into bounds */ if(!(c->flags & (SUB_CLIENT_MODE_FULL|SUB_CLIENT_TYPE_DOCK))) { int maxx = 0, maxy = 0; /* Check size for clients we are allowed to change */ if(!(c->flags & SUB_CLIENT_MODE_FIXED)) { if(c->geom.width > bounds->width) c->geom.width = bounds->width; if(c->geom.height > bounds->height) c->geom.height = bounds->height; } /* Check whether window fits into bounds */ maxx = bounds->x + bounds->width; maxy = bounds->y + bounds->height; /* Check x and center */ if(c->geom.x < bounds->x || c->geom.x > maxx || c->geom.x + c->geom.width > maxx) { if(c->flags & SUB_CLIENT_MODE_FLOAT) c->geom.x = bounds->x + ((bounds->width - c->geom.width) / 2); else c->geom.x = bounds->x; } /* Check y and center */ if(c->geom.y < bounds->y || c->geom.y > maxy || c->geom.y + c->geom.height > maxy) { if(c->flags & SUB_CLIENT_MODE_FLOAT) c->geom.y = bounds->y + ((bounds->height - c->geom.height) / 2); else c->geom.y = bounds->y; } } } /* }}} */ /** subClientRestack {{{ * @brief Restack client * @param[in] c A #SubClient * @param[in] dir Either below or above **/ void subClientRestack(SubClient *c, int dir) { c->dir = dir; subArraySort(subtle->clients, ClientCompare); c->dir = -1; subClientPublish(True); subSubtleLogDebugSubtle("Restack: instance=%s, win=%#lx, dir=%s\n", c->instance, c->win, SUB_CLIENT_RESTACK_DOWN == dir ? "down" : "up"); } /* }}} */ /** subClientArrange {{{ * @brief Arrange position of client * @param[in] c A #SubClient * @param[in] gravity The gravity id * @param[in] screen The screen id **/ void subClientArrange(SubClient *c, int gravityid, int screenid) { SubScreen *s = SCREEN(subArrayGet(subtle->screens, screenid)); DEAD(c); assert(c && s); /* Check flags */ if(c->flags & SUB_CLIENT_MODE_FULL) { /* Use all screens when in zaphod mode */ if(c->flags & SUB_CLIENT_MODE_ZAPHOD) { XMoveResizeWindow(subtle->dpy, c->win, 0, 0, subtle->width, subtle->height); } else XMoveResizeWindow(subtle->dpy, c->win, s->base.x, s->base.y, s->base.width, s->base.height); XRaiseWindow(subtle->dpy, c->win); } else if(c->flags & SUB_CLIENT_MODE_FLOAT) { if(c->flags & SUB_CLIENT_ARRANGE || (-1 != screenid && c->screenid != screenid)) { SubScreen *s2 = SCREEN(subArrayGet(subtle->screens, -1 != c->screenid ? c->screenid : 0)); /* Update screen offsets */ if(s != s2) { c->geom.x = c->geom.x - s2->geom.x + s->geom.x; c->geom.y = c->geom.y - s2->geom.y + s->geom.y; c->geom.width = c->geom.width; c->geom.height = c->geom.height; c->screenid = screenid; } /* Finally resize window */ subClientResize(c, &(s->geom), True); XMoveResizeWindow(subtle->dpy, c->win, c->geom.x, c->geom.y, c->geom.width, c->geom.height); } } else if(c->flags & SUB_CLIENT_TYPE_DESKTOP) { c->geom = s->geom; /* Just use screen size for desktop windows */ XMoveResizeWindow(subtle->dpy, c->win, c->geom.x, c->geom.y, c->geom.width, c->geom.height); XLowerWindow(subtle->dpy, c->win); } else if(c->flags & SUB_CLIENT_TYPE_DOCK) { /* Just use screen size for desktop windows */ XMoveResizeWindow(subtle->dpy, c->win, c->geom.x, c->geom.y, c->geom.width, c->geom.height); XLowerWindow(subtle->dpy, c->win); } else { if(c->flags & SUB_CLIENT_ARRANGE || c->gravityid != gravityid || c->screenid != screenid) { XRectangle bounds = s->geom; int old_gravity = c->gravityid, old_screen = c->screenid; SubGravity *g = NULL, *old_g = NULL; /* Set values */ if(-1 != screenid) c->screenid = screenid; if(-1 != gravityid) c->gravityid = c->gravities[s->viewid] = gravityid; g = GRAVITY(subArrayGet(subtle->gravities, gravityid)); old_g = GRAVITY(subArrayGet(subtle->gravities, old_gravity)); /* Gravity tiling */ if(-1 != old_screen && (subtle->flags & SUB_SUBTLE_TILING || (old_g && old_g->flags & (SUB_GRAVITY_HORZ|SUB_GRAVITY_VERT)))) ClientTile(old_gravity, old_screen); if(subtle->flags & SUB_SUBTLE_TILING || (g && g->flags & (SUB_GRAVITY_HORZ|SUB_GRAVITY_VERT))) { ClientTile(gravityid, -1 == screenid ? 0 : screenid); } else { /* Set size for bounds*/ if(c->flags & SUB_CLIENT_MODE_ZAPHOD) ClientZaphod(c, &bounds); subGravityGeometry(g, &bounds, &c->geom); ClientResize(c, &bounds); } /* EWMH: Gravity */ subEwmhSetCardinals(c->win, SUB_EWMH_SUBTLE_CLIENT_GRAVITY, (long *)&c->gravityid, 1); XSync(subtle->dpy, False); ///< Sync before going on /* Hook: Gravity */ subHookCall((SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_GRAVITY), (void *)c); } } } /* }}} */ /** subClientToggle {{{ * @brief Toggle various states of client * @param[in] c A #SubClient * @param[in] flags Toggle flag * @param[in] set_gravity Whether gravity should be set **/ void subClientToggle(SubClient *c, int flags, int set_gravity) { int nstates = 0; Atom states[3] = { None }; DEAD(c); assert(c); /* Set arrange flags for certain modes */ if(flags & (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_STICK|SUB_CLIENT_MODE_FULL| SUB_CLIENT_MODE_ZAPHOD|SUB_CLIENT_MODE_BORDERLESS|SUB_CLIENT_MODE_CENTER)) c->flags |= SUB_CLIENT_ARRANGE; ///< Force rearrange /* Handle sticky mode */ if(flags & SUB_CLIENT_MODE_STICK) { SubClient *focus = NULL; /* Unset stick mode */ if(c->flags & SUB_CLIENT_MODE_STICK) { /* Update highlight urgent client */ if(c->flags & SUB_CLIENT_MODE_URGENT) subtle->urgent_tags &= ~c->tags; } else { /* Check if gravity should be set */ if(set_gravity) { int i; /* Set gravity for untagged views */ for(i = 0; i < subtle->views->ndata; i++) { SubView *v = VIEW(subtle->views->data[i]); /* Check visibility manually */ if(!(v->tags & c->tags) && -1 != c->gravityid) c->gravities[i] = c->gravityid; } } /* Set screen when required*/ if(!(c->flags & SUB_CLIENT_MODE_STICK_SCREEN)) { /* Find screen: Prefer screen of current window */ if(subtle->flags & SUB_SUBTLE_SKIP_WARP && (focus = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID))) && VISIBLE(focus)) c->screenid = focus->screenid; else subScreenCurrent(&c->screenid); } } } /* Handle fullscreen mode */ if(flags & SUB_CLIENT_MODE_FULL) { if(c->flags & SUB_CLIENT_MODE_FULL) { if(!(c->flags & SUB_CLIENT_MODE_BORDERLESS)) XSetWindowBorderWidth(subtle->dpy, c->win, subtle->styles.clients.border.top); } else { /* Normally, you'd expect, that a fixed size window wants to keep * the size. Apparently, some broken clients just violate that, so we * exclude fixed windows with min != screen size from fullscreen */ if(c->flags & SUB_CLIENT_MODE_FIXED) { SubScreen *s = SCREEN(subtle->screens->data[c->screenid]); if(s->base.width != c->minw || s->base.height != c->minh) { flags &= ~SUB_CLIENT_MODE_FULL; return; } } XSetWindowBorderWidth(subtle->dpy, c->win, 0); } } /* Handle borderless mode */ if(flags & SUB_CLIENT_MODE_BORDERLESS) { /* Unset borderless or fullscreen mode */ if(!(c->flags & SUB_CLIENT_MODE_BORDERLESS)) XSetWindowBorderWidth(subtle->dpy, c->win, subtle->styles.clients.border.top); else XSetWindowBorderWidth(subtle->dpy, c->win, 0); } /* Handle urgent mode */ if(flags & SUB_CLIENT_MODE_URGENT) subtle->urgent_tags |= c->tags; /* Handle center mode */ if(flags & SUB_CLIENT_MODE_CENTER) { if(c->flags & SUB_CLIENT_MODE_CENTER) { c->flags &= ~SUB_CLIENT_MODE_FLOAT; c->flags |= SUB_CLIENT_ARRANGE; } else { SubScreen *s = SCREEN(subtle->screens->data[c->screenid]); /* Set to screen center */ c->geom.x = s->geom.x + (s->geom.width - c->geom.width - 2 * BORDER(c)) / 2; c->geom.y = s->geom.y + (s->geom.height - c->geom.height - 2 * BORDER(c)) / 2; flags |= SUB_CLIENT_MODE_FLOAT; c->flags |= SUB_CLIENT_ARRANGE; } } /* Handle desktop and dock type (one way) */ if(flags & (SUB_CLIENT_TYPE_DESKTOP|SUB_CLIENT_TYPE_DOCK)) { XSetWindowBorderWidth(subtle->dpy, c->win, 0); /* Special treatment */ if(flags & SUB_CLIENT_TYPE_DESKTOP) { SubScreen *s = SCREEN(subtle->screens->data[c->screenid]); c->geom = s->base; /* Add panel heights without struts */ if(s->flags & SUB_SCREEN_PANEL1) { c->geom.y += subtle->ph; c->geom.height -= subtle->ph; } if(s->flags & SUB_SCREEN_PANEL2) c->geom.height -= subtle->ph; } } /* Finally toggle mode flags only */ c->flags = ((c->flags & ~MODES_ALL) | ((c->flags & MODES_ALL) ^ (flags & MODES_ALL))); /* Sort for keeping stacking order */ if(c->flags & (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_FULL| SUB_CLIENT_TYPE_DESKTOP|SUB_CLIENT_TYPE_DOCK)) subClientRestack(c, SUB_CLIENT_RESTACK_UP); /* EWMH: State and flags */ if(c->flags & SUB_CLIENT_MODE_FULL) states[nstates++] = subEwmhGet(SUB_EWMH_NET_WM_STATE_FULLSCREEN); if(c->flags & SUB_CLIENT_MODE_FLOAT) states[nstates++] = subEwmhGet(SUB_EWMH_NET_WM_STATE_ABOVE); if(c->flags & SUB_CLIENT_MODE_STICK) states[nstates++] = subEwmhGet(SUB_EWMH_NET_WM_STATE_STICKY); if(c->flags & SUB_CLIENT_MODE_URGENT) states[nstates++] = subEwmhGet(SUB_EWMH_NET_WM_STATE_ATTENTION); subEwmhTranslateClientMode(c->flags, &flags); XChangeProperty(subtle->dpy, c->win, subEwmhGet(SUB_EWMH_NET_WM_STATE), XA_ATOM, 32, PropModeReplace, (unsigned char *)&states, nstates); subEwmhSetCardinals(c->win, SUB_EWMH_SUBTLE_CLIENT_FLAGS, (long *)&flags, 1); XSync(subtle->dpy, False); ///< Sync all changes /* Hook: Mode */ subHookCall((SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_MODE), (void *)c); subSubtleLogDebugSubtle("Toggle: flags=%d, set_gravity=%d\n", flags, set_gravity); } /* }}} */ /** subClientSetStrut {{{ * @brief Set client strut * @param[in] c A #SubClient **/ void subClientSetStrut(SubClient *c) { unsigned long size = 0; long *strut = NULL; DEAD(c); assert(c); /* Get strut property */ if((strut = (long *)subSharedPropertyGet(subtle->dpy, c->win, XA_CARDINAL, subEwmhGet(SUB_EWMH_NET_WM_STRUT), &size))) { if(4 == size) ///< Only complete struts { subtle->styles.subtle.padding.left = MAX(subtle->styles.subtle.padding.left, strut[0]); subtle->styles.subtle.padding.right = MAX(subtle->styles.subtle.padding.right, strut[1]); subtle->styles.subtle.padding.top = MAX(subtle->styles.subtle.padding.top, strut[2]); subtle->styles.subtle.padding.bottom = MAX(subtle->styles.subtle.padding.bottom, strut[3]); /* Update screen and clients */ subScreenResize(); subScreenConfigure(); } XFree(strut); } } /* }}} */ /** subClientSetProtocols {{{ * @brief Set client protocols * @param[in] c A #SubClient **/ void subClientSetProtocols(SubClient *c) { int i, n = 0; Atom *protos = NULL; assert(c); /* Window manager protocols */ if(XGetWMProtocols(subtle->dpy, c->win, &protos, &n)) { for(i = 0; i < n; i++) { switch(subEwmhFind(protos[i])) { case SUB_EWMH_WM_TAKE_FOCUS: c->flags |= SUB_CLIENT_FOCUS; break; case SUB_EWMH_WM_DELETE_WINDOW: c->flags |= SUB_CLIENT_CLOSE; break; default: break; } } XFree(protos); } } /* }}} */ /** subClientSetSizeHints {{{ * @brief Set client size hints * @param[in] c A #SubClient * @param[in] flags Mode flags **/ void subClientSetSizeHints(SubClient *c, int *flags) { long supplied = 0; XSizeHints *hints = NULL; SubScreen *s = NULL; DEAD(c); assert(c); if(!(hints = XAllocSizeHints())) { subSubtleLogError("Cannot alloc memory. Exhausted?\n"); abort(); } s = SCREEN(subtle->screens->data[0]); ///< Assume first screen /* Default values {{{ */ c->minw = MINW; c->minh = MINH; c->maxw = -1; c->maxh = -1; c->minr = 0.0f; c->maxr = 0.0f; c->incw = 1; c->inch = 1; c->basew = 0; c->baseh = 0; /* }}} */ /* Size hints - no idea why it's called normal hints */ if(XGetWMNormalHints(subtle->dpy, c->win, hints, &supplied)) { /* Program min size */ if(hints->flags & PMinSize) { /* Limit min size to screen size if larger */ if(hints->min_width) c->minw = c->minw > s->geom.width ? s->geom.width : MAX(MINW, hints->min_width); if(hints->min_height) c->minh = c->minh > s->geom.height ? s->geom.height : MAX(MINH, hints->min_height); } /* Program max size */ if(hints->flags & PMaxSize) { /* Limit max size to screen size if larger */ if(hints->max_width) c->maxw = hints->max_width > s->geom.width ? s->geom.width : hints->max_width; if(hints->max_height) c->maxh = hints->max_height > s->geom.height - subtle->ph ? s->geom.height - subtle->ph : hints->max_height; } /* Set float when min == max size (EWMH: Fixed size windows) */ if(hints->flags & PMinSize && hints->flags & PMaxSize) { if(hints->min_width == hints->max_width && hints->min_height == hints->max_height && !(c->flags & SUB_CLIENT_TYPE_DESKTOP)) *flags |= (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_FIXED); } /* Aspect ratios */ if(hints->flags & PAspect) { if(hints->min_aspect.y) c->minr = (float)hints->min_aspect.x / hints->min_aspect.y; if(hints->max_aspect.y) c->maxr = (float)hints->max_aspect.x / hints->max_aspect.y; } /* Resize increment steps */ if(hints->flags & PResizeInc) { if(hints->width_inc) c->incw = hints->width_inc; if(hints->height_inc) c->inch = hints->height_inc; } /* Base sizes */ if(hints->flags & PBaseSize) { if(hints->base_width) c->basew = hints->base_width; if(hints->base_height) c->baseh = hints->base_height; } /* Check for specific position */ if((subtle->flags & SUB_SUBTLE_RESIZE || c->flags & (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_RESIZE|SUB_CLIENT_TYPE_DOCK))) { /* User/program size */ if(hints->flags & (USSize|PSize)) { c->geom.width = hints->width; c->geom.height = hints->height; } /* User/program position */ if(hints->flags & (USPosition|PPosition)) { c->geom.x = hints->x; c->geom.y = hints->y; } /* Sanitize positions for stupid clients like GIMP */ if(hints->flags & (USSize|PSize|USPosition|PPosition)) subClientResize(c, &(s->geom), True); } } XFree(hints); subSubtleLogDebug("SetSizeHints: x=%d, y=%d, width=%d, height=%d, " "minw=%d, minh=%d, maxw=%d, maxh=%d, minr=%.1f, maxr=%.1f, " "incw=%d, inch=%d, basew=%d, baseh=%d\n", c->geom.x, c->geom.y, c->geom.width, c->geom.height, c->minw, c->minh, c->maxw, c->maxh, c->minr, c->maxr, c->incw, c->inch, c->basew, c->baseh); } /* }}} */ /** subClientSetWMHints {{{ * @brief Set client WM hints * @param[in] c A #SubClient * @param[in] flags Mode flags **/ void subClientSetWMHints(SubClient *c, int *flags) { XWMHints *hints = NULL; assert(c && flags); /* Window manager hints (ICCCM 4.1.7) */ if((hints = XGetWMHints(subtle->dpy, c->win))) { /* Handle urgency hint: * Set urgency if window hasn't focus and and * remove it after getting focus */ if(hints->flags & XUrgencyHint && c->win != subtle->windows.focus[0]) { *flags |= SUB_CLIENT_MODE_URGENT; } /* Handle window group hint */ if(hints->flags & WindowGroupHint) { SubClient *k = NULL; /* Copy tags and modes */ if((k = CLIENT(subSubtleFind(hints->window_group, CLIENTID)))) { *flags |= (k->flags & MODES_ALL); c->tags |= k->tags; c->screenid |= k->screenid; } } /* Handle just false value of input hint since it is default */ if(hints->flags & InputHint && !hints->input) c->flags &= ~SUB_CLIENT_INPUT; XFree(hints); } subSubtleLogDebugSubtle("SetWMHints\n"); } /* }}} */ /** subClientSetMWMHints {{{ * @brief Set client MWM hints * @param[in] c A #SubClient **/ void subClientSetMWMHints(SubClient *c) { unsigned long size = 0; ClientMWMHints *hints = NULL; assert(c); /* Window manager hints */ if((hints = (ClientMWMHints *)subSharedPropertyGet(subtle->dpy, c->win, subEwmhGet(SUB_EWMH_MOTIF_WM_HINTS), subEwmhGet(SUB_EWMH_MOTIF_WM_HINTS), &size))) { /* Check if hints contain decoration flags */ if(hints->flags & MWM_FLAG_DECORATIONS) { /* Check window border */ if(!(hints->decorations & (MWM_DECOR_ALL|MWM_DECOR_BORDER))) c->flags |= SUB_CLIENT_MODE_BORDERLESS; } free(hints); } subSubtleLogDebugSubtle("SetMWMHints\n"); } /* }}} */ /** subClientSetState {{{ * @brief Set client WM state * @param[in] c A #SubClient * @param[in] flags Mode flags **/ void subClientSetState(SubClient *c, int *flags) { int i; unsigned long nstates = 0; Atom *states = NULL; assert(c); /* Window state */ if((states = (Atom *)subSharedPropertyGet(subtle->dpy, c->win, XA_ATOM, subEwmhGet(SUB_EWMH_NET_WM_STATE), &nstates))) { for(i = 0; i < nstates; i++) subEwmhTranslateWMState(states[i], flags); XFree(states); } subSubtleLogDebugSubtle("SetState\n"); } /* }}} */ /** subClientSetTransient {{{ * @brief Set client transient * @param[in] c A #SubClient * @param[in] flags Mode flags **/ void subClientSetTransient(SubClient *c, int *flags) { Window trans = None; assert(c && flags); /* Check for transient windows */ if(XGetTransientForHint(subtle->dpy, c->win, &trans)) { SubClient *k = NULL; /* Check if transient windows should be urgent */ *flags |= subtle->flags & SUB_SUBTLE_URGENT ? SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_URGENT : SUB_CLIENT_MODE_FLOAT; /* Find parent window */ if((k = CLIENT(subSubtleFind(trans, CLIENTID)))) { *flags |= (k->flags & MODES_ALL); c->tags |= k->tags; c->screenid |= k->screenid; } } subSubtleLogDebugSubtle("SetTransient\n"); } /* }}} */ /** subClientSetType {{{ * @brief Set client type * @param[in] c A #SubClient * @param[in] flags Mode flags **/ void subClientSetType(SubClient *c, int *flags) { int i; unsigned long size = 0; Atom *types = NULL; assert(c); /* Get window type */ if((types = (Atom *)subSharedPropertyGet(subtle->dpy, c->win, XA_ATOM, subEwmhGet(SUB_EWMH_NET_WM_WINDOW_TYPE), &size))) { int id = 0; /* Set flags according to window types */ for(i = 0; i < size; i++) { switch((id = subEwmhFind(types[i]))) { case SUB_EWMH_NET_WM_WINDOW_TYPE_DESKTOP: c->flags |= SUB_CLIENT_TYPE_DESKTOP; *flags |= (SUB_CLIENT_MODE_FIXED|SUB_CLIENT_MODE_STICK); break; case SUB_EWMH_NET_WM_WINDOW_TYPE_DOCK: c->flags |= SUB_CLIENT_TYPE_DOCK; *flags |= (SUB_CLIENT_MODE_FIXED|SUB_CLIENT_MODE_STICK); break; case SUB_EWMH_NET_WM_WINDOW_TYPE_TOOLBAR: c->flags |= SUB_CLIENT_TYPE_TOOLBAR; break; case SUB_EWMH_NET_WM_WINDOW_TYPE_SPLASH: c->flags |= SUB_CLIENT_TYPE_SPLASH; *flags = (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_CENTER); break; case SUB_EWMH_NET_WM_WINDOW_TYPE_DIALOG: c->flags |= SUB_CLIENT_TYPE_DIALOG; *flags |= (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_CENTER); break; default: break; } } XFree(types); } /* Set normal type */ if(!(c->flags & TYPES_ALL)) c->flags |= SUB_CLIENT_TYPE_NORMAL; subSubtleLogDebugSubtle("SetType\n"); } /* }}} */ /** subClientClose {{{ * @brief Send client delete message or just kill it * @param[in] c A #SubClient **/ void subClientClose(SubClient *c) { assert(c); /* Honor window preferences (see ICCCM 4.1.2.7, 4.2.8.1) */ if(c->flags & SUB_CLIENT_CLOSE) { subEwmhMessage(c->win, SUB_EWMH_WM_PROTOCOLS, NoEventMask, subEwmhGet(SUB_EWMH_WM_DELETE_WINDOW), CurrentTime, 0, 0, 0); } else { int sid = (subtle->windows.focus[0] == c->win ? c->screenid : -1); ///< Save /* Kill it manually */ XKillClient(subtle->dpy, c->win); subArrayRemove(subtle->clients, (void *)c); subClientKill(c); subClientPublish(False); subScreenConfigure(); subScreenUpdate(); subScreenRender(); /* Update focus if necessary */ if(-1 != sid) { SubClient *k = subClientNext(sid, False); if(k) subClientFocus(k, True); } } subSubtleLogDebugSubtle("Close\n"); } /* }}} */ /** subClientKill {{{ * @brief Kill a client * @param[in] c A #SubClient **/ void subClientKill(SubClient *c) { assert(c); /* Hook: Kill */ subHookCall((SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_KILL), (void *)c); /* Remove _NET_WM_STATE (see EWMH 1.3) */ subSharedPropertyDelete(subtle->dpy, c->win, subEwmhGet(SUB_EWMH_NET_WM_STATE)); /* Ignore further events and delete context */ XSelectInput(subtle->dpy, c->win, NoEventMask); XDeleteContext(subtle->dpy, c->win, CLIENTID); /* Remove client tags from urgent tags */ if(c->flags & SUB_CLIENT_MODE_URGENT) subtle->urgent_tags &= ~c->tags; /* Tile remaining clients if necessary */ if(VISIBLE(c)) { SubGravity *g = GRAVITY(subArrayGet(subtle->gravities, c->gravityid)); if(g && ((subtle->flags & SUB_SUBTLE_TILING) || g->flags & (SUB_GRAVITY_HORZ|SUB_GRAVITY_VERT))) ClientTile(c->gravityid, c->screenid); } if(c->gravities) free(c->gravities); if(c->name) free(c->name); if(c->instance) free(c->instance); if(c->klass) free(c->klass); if(c->role) free(c->role); free(c); subSubtleLogDebugSubtle("Kill\n"); } /* }}} */ /* All */ /** subClientPublish {{{ * @brief Publish clients * @param[in] restack Restack windows **/ void subClientPublish(int restack) { int i; Window *wins = (Window *)subSharedMemoryAlloc(subtle->clients->ndata, sizeof(Window)); /* Sort clients from top (=> 0) to bottom */ for(i = 0; i < subtle->clients->ndata; i++) wins[subtle->clients->ndata - 1 - i] = CLIENT(subtle->clients->data[i])->win; /* EWMH: Client list and client list stacking (same for us) */ subEwmhSetWindows(ROOT, SUB_EWMH_NET_CLIENT_LIST, wins, subtle->clients->ndata); subEwmhSetWindows(ROOT, SUB_EWMH_NET_CLIENT_LIST_STACKING, wins, subtle->clients->ndata); /* Restack windows? We assembled the array anyway. */ if(restack) XRestackWindows(subtle->dpy, wins, subtle->clients->ndata); XSync(subtle->dpy, False); ///< Sync all changes free(wins); subSubtleLogDebugSubtle("Publish: clients=%d, restack=%d\n", subtle->clients->ndata, restack); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/ewmh.c0000644000175000017500000003062411770332063017410 0ustar formorerformorer /** * @package subtle * * @file EWMH functions * @copyright Copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/ewmh.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include #include #include "subtle.h" static Atom atoms[SUB_EWMH_TOTAL]; /* Typedef {{{ */ typedef struct xembedinfo_t { CARD32 version, flags; } XEmbedInfo; /* }}} */ /** subEwmhInit {{{ * @brief Init and register ICCCM/EWMH atoms **/ void subEwmhInit(void) { int len = 0; long data[2] = { 0, 0 }, pid = (long)getpid(); char *selection = NULL, *names[] = { /* ICCCM */ "WM_NAME", "WM_CLASS", "WM_STATE", "WM_PROTOCOLS", "WM_TAKE_FOCUS", "WM_DELETE_WINDOW", "WM_NORMAL_HINTS", "WM_SIZE_HINTS", "WM_HINTS", "WM_WINDOW_ROLE", "WM_CLIENT_LEADER", /* EWMH */ "_NET_SUPPORTED", "_NET_CLIENT_LIST", "_NET_CLIENT_LIST_STACKING", "_NET_NUMBER_OF_DESKTOPS", "_NET_DESKTOP_NAMES", "_NET_DESKTOP_GEOMETRY", "_NET_DESKTOP_VIEWPORT", "_NET_CURRENT_DESKTOP", "_NET_ACTIVE_WINDOW", "_NET_WORKAREA", "_NET_SUPPORTING_WM_CHECK", "_NET_WM_FULL_PLACEMENT", "_NET_FRAME_EXTENTS", /* Client */ "_NET_CLOSE_WINDOW", "_NET_RESTACK_WINDOW", "_NET_MOVERESIZE_WINDOW", "_NET_WM_NAME", "_NET_WM_PID", "_NET_WM_DESKTOP", "_NET_WM_STRUT", /* Types */ "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_DOCK", "_NET_WM_WINDOW_TYPE_DESKTOP", "_NET_WM_WINDOW_TYPE_TOOLBAR", "_NET_WM_WINDOW_TYPE_SPLASH", "_NET_WM_WINDOW_TYPE_DIALOG", /* States */ "_NET_WM_STATE", "_NET_WM_STATE_FULLSCREEN", "_NET_WM_STATE_ABOVE", "_NET_WM_STATE_STICKY", "_NET_WM_STATE_DEMANDS_ATTENTION", /* Tray */ "_NET_SYSTEM_TRAY_OPCODE", "_NET_SYSTEM_TRAY_MESSAGE_DATA", "_NET_SYSTEM_TRAY_S", /* Misc */ "UTF8_STRING", "MANAGER", "_MOTIF_WM_HINTS", /* XEmbed */ "_XEMBED", "_XEMBED_INFO", /* subtle */ "SUBTLE_CLIENT_TAGS", "SUBTLE_CLIENT_RETAG", "SUBTLE_CLIENT_GRAVITY", "SUBTLE_CLIENT_SCREEN", "SUBTLE_CLIENT_FLAGS", "SUBTLE_GRAVITY_NEW", "SUBTLE_GRAVITY_FLAGS", "SUBTLE_GRAVITY_LIST", "SUBTLE_GRAVITY_KILL", "SUBTLE_TAG_NEW", "SUBTLE_TAG_LIST", "SUBTLE_TAG_KILL", "SUBTLE_TRAY_LIST", "SUBTLE_VIEW_NEW", "SUBTLE_VIEW_TAGS", "SUBTLE_VIEW_STYLE", "SUBTLE_VIEW_ICONS", "SUBTLE_VIEW_KILL", "SUBTLE_SUBLET_UPDATE", "SUBTLE_SUBLET_DATA", "SUBTLE_SUBLET_STYLE", "SUBTLE_SUBLET_FLAGS", "SUBTLE_SUBLET_LIST", "SUBTLE_SUBLET_KILL", "SUBTLE_SCREEN_PANELS", "SUBTLE_SCREEN_VIEWS", "SUBTLE_SCREEN_JUMP", "SUBTLE_VISIBLE_TAGS", "SUBTLE_VISIBLE_VIEWS", "SUBTLE_RENDER", "SUBTLE_RELOAD", "SUBTLE_RESTART", "SUBTLE_QUIT", "SUBTLE_COLORS", "SUBTLE_FONT", "SUBTLE_DATA", "SUBTLE_VERSION" }; assert(SUB_EWMH_TOTAL == LENGTH(names)); /* Update tray selection name for current screen */ len = strlen(names[SUB_EWMH_NET_SYSTEM_TRAY_SELECTION]) + 5; ///< For high screen counts selection = (char *)subSharedMemoryAlloc(len, sizeof(char)); snprintf(selection, len, "%s%u", names[SUB_EWMH_NET_SYSTEM_TRAY_SELECTION], SCRN); subSubtleLogDebug("Selection: len=%d, name=%s\n", len, selection); names[SUB_EWMH_NET_SYSTEM_TRAY_SELECTION] = selection; /* Register atoms */ XInternAtoms(subtle->dpy, names, SUB_EWMH_TOTAL, 0, atoms); subtle->flags |= SUB_SUBTLE_EWMH; ///< Set EWMH flag free(selection); /* EWMH: Supported hints */ XChangeProperty(subtle->dpy, ROOT, atoms[SUB_EWMH_NET_SUPPORTED], XA_ATOM, 32, PropModeReplace, (unsigned char *)&atoms, SUB_EWMH_TOTAL); /* EWMH: Window manager information */ subEwmhSetWindows(ROOT, SUB_EWMH_NET_SUPPORTING_WM_CHECK, &subtle->windows.support, 1); subEwmhSetString(subtle->windows.support, SUB_EWMH_NET_WM_NAME, PKG_NAME); subEwmhSetString(subtle->windows.support, SUB_EWMH_WM_CLASS, PKG_NAME); subEwmhSetCardinals(subtle->windows.support, SUB_EWMH_NET_WM_PID, &pid, 1); subEwmhSetString(subtle->windows.support, SUB_EWMH_SUBTLE_VERSION, PKG_VERSION); /* EWMH: Desktop geometry */ data[0] = subtle->width; data[1] = subtle->height; subEwmhSetCardinals(ROOT, SUB_EWMH_NET_DESKTOP_GEOMETRY, (long *)&data, 2); /* EWMH: Client list and client list stacking */ subEwmhSetWindows(ROOT, SUB_EWMH_NET_CLIENT_LIST, NULL, 0); subEwmhSetWindows(ROOT, SUB_EWMH_NET_CLIENT_LIST_STACKING, NULL, 0); subSubtleLogDebugSubtle("Init\n"); } /* }}} */ /** subEwmhGet {{{ * @brief Find intern atoms * @param[in] e A #SubEwmh * @return Returns the desired #Atom **/ Atom subEwmhGet(SubEwmh e) { assert(e <= SUB_EWMH_TOTAL); return atoms[e]; } /* }}} */ /** subEwmhFind {{{ * @brief Find id for intern atom * @param[in] e A #SubEwmh * @retval >=0 Found index * @retval -1 Atom was not found **/ SubEwmh subEwmhFind(Atom atom) { int i; for(i = 0; atom && i < SUB_EWMH_TOTAL; i++) if(atoms[i] == atom) return i; return -1; } /* }}} */ /** subEwmhGetWMState {{{ * @brief Get WM state from window * @param[in] win A window * @return Returns window WM state **/ long subEwmhGetWMState(Window win) { Atom type = None; int format = 0; unsigned long unused, bytes; long *data = NULL, state = WithdrawnState; assert(win); if(Success == XGetWindowProperty(subtle->dpy, win, atoms[SUB_EWMH_WM_STATE], 0L, 2L, False, atoms[SUB_EWMH_WM_STATE], &type, &format, &bytes, &unused, (unsigned char **)&data) && bytes) { state = *data; XFree(data); } return state; } /* }}} */ /** subEwmhGetXEmbedState {{{ * @brief Get window Xembed state * @param[in] win A window **/ long subEwmhGetXEmbedState(Window win) { long flags = 0; XEmbedInfo *info = NULL; /* Get xembed data */ if((info = (XEmbedInfo *)subSharedPropertyGet(subtle->dpy, win, subEwmhGet(SUB_EWMH_XEMBED_INFO), subEwmhGet(SUB_EWMH_XEMBED_INFO), NULL))) { flags = (long)info->flags; subSubtleLogDebug("XEmbedInfo: win=%#lx, version=%ld, flags=%ld, mapped=%ld\n", win, info->version, info->flags, info->flags & XEMBED_MAPPED); XFree(info); } return flags; } /* }}} */ /** subEwmhSetWindows {{{ * @brief Change window property * @param[in] win Window * @param[in] e A #SubEwmh * @param[in] values Window list * @param[in] size Size of the list **/ void subEwmhSetWindows(Window win, SubEwmh e, Window *values, int size) { XChangeProperty(subtle->dpy, win, atoms[e], XA_WINDOW, 32, PropModeReplace, (unsigned char *)values, size); } /* }}} */ /** subEwmhSetCardinals {{{ * @brief Change window property * @param[in] win Window * @param[in] e A #SubEwmh * @param[in] values Cardinal list * @param[in] size Size of the list **/ void subEwmhSetCardinals(Window win, SubEwmh e, long *values, int size) { XChangeProperty(subtle->dpy, win, atoms[e], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)values, size); } /* }}} */ /** subEwmhSetString {{{ * @brief Change window property * @param[in] win Window * @param[in] e A #SubEwmh * @param[in] value String value **/ void subEwmhSetString(Window win, SubEwmh e, char *value) { XChangeProperty(subtle->dpy, win, atoms[e], atoms[SUB_EWMH_UTF8], 8, PropModeReplace, (unsigned char *)value, strlen(value)); } /* }}} */ /** subEwmhSetWMState {{{ * @brief Set WM state for window * @param[in] win A window * @param[in] state New state for the window **/ void subEwmhSetWMState(Window win, long state) { CARD32 data[2]; data[0] = state; data[1] = None; /* No icons */ assert(win); XChangeProperty(subtle->dpy, win, atoms[SUB_EWMH_WM_STATE], atoms[SUB_EWMH_WM_STATE], 32, PropModeReplace, (unsigned char *)data, 2); } /* }}} */ /** subEwmhTranslateWMState {{{ * @brief Translate WM state to flags * @param[in] atom A window * @param[inout] flags Translated flags **/ void subEwmhTranslateWMState(Atom atom, int *flags) { /* Translate supported WM states */ switch(subEwmhFind(atom)) { case SUB_EWMH_NET_WM_STATE_FULLSCREEN: *flags |= SUB_CLIENT_MODE_FULL; break; case SUB_EWMH_NET_WM_STATE_ABOVE: *flags |= SUB_CLIENT_MODE_FLOAT; break; case SUB_EWMH_NET_WM_STATE_STICKY: *flags |= SUB_CLIENT_MODE_STICK; break; case SUB_EWMH_NET_WM_STATE_ATTENTION: *flags |= SUB_CLIENT_MODE_URGENT; break; default: break; } } /* }}} */ /** subEwmhTranslateClientMode {{{ * @brief Translate mode flags to ewmh flags * @param[in] client_flags Client mode flags * @param[inout] flags Translated flags **/ void subEwmhTranslateClientMode(int client_flags, int *flags) { /* Translate supported mode flags */ if(client_flags & SUB_CLIENT_MODE_FULL) *flags |= SUB_EWMH_FULL; if(client_flags & SUB_CLIENT_MODE_FLOAT) *flags |= SUB_EWMH_FLOAT; if(client_flags & SUB_CLIENT_MODE_STICK) *flags |= SUB_EWMH_STICK; if(client_flags & SUB_CLIENT_MODE_RESIZE) *flags |= SUB_EWMH_RESIZE; if(client_flags & SUB_CLIENT_MODE_URGENT) *flags |= SUB_EWMH_URGENT; if(client_flags & SUB_CLIENT_MODE_ZAPHOD) *flags |= SUB_EWMH_ZAPHOD; if(client_flags & SUB_CLIENT_MODE_FIXED) *flags |= SUB_EWMH_FIXED; if(client_flags & SUB_CLIENT_MODE_BORDERLESS) *flags |= SUB_EWMH_BORDERLESS; } /* }}} */ /** subEwmhMessage {{{ * @brief Send message * @param[in] win Target window * @param[in] e A #SubEwmh * @param[in] mask Event mask * @param[in] data0 Data part 1 * @param[in] data1 Data part 2 * @param[in] data2 Data part 3 * @param[in] data3 Data part 4 * @param[in] data4 Data part 5 * @retval 0 Error * @retval >0 Success **/ int subEwmhMessage(Window win, SubEwmh e, long mask, long data0, long data1, long data2, long data3, long data4) { XClientMessageEvent ev; /* Assemble message */ ev.type = ClientMessage; ev.message_type = atoms[e]; ev.window = win; ev.format = 32; ev.data.l[0] = data0; ev.data.l[1] = data1; ev.data.l[2] = data2; ev.data.l[3] = data3; ev.data.l[4] = data4; return XSendEvent(subtle->dpy, win, False, mask, (XEvent *)&ev); } /* }}} */ /** subEwmhFinish {{{ * @brief Delete set ICCCM/EWMH atoms **/ void subEwmhFinish(void) { /* Delete root properties on real shutdown */ if(subtle->flags & SUB_SUBTLE_EWMH) { /* EWMH properties */ subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_SUPPORTED)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_SUPPORTING_WM_CHECK)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_ACTIVE_WINDOW)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_CURRENT_DESKTOP)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_DESKTOP_NAMES)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_NUMBER_OF_DESKTOPS)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_DESKTOP_VIEWPORT)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_DESKTOP_GEOMETRY)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_WORKAREA)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_CLIENT_LIST)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_CLIENT_LIST_STACKING)); /* subtle extension */ subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_GRAVITY_LIST)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_TAG_LIST)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_TRAY_LIST)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_VIEW_TAGS)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_COLORS)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_FONT)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_SUBLET_LIST)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_SCREEN_VIEWS)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_VISIBLE_VIEWS)); subSharedPropertyDelete(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_VISIBLE_TAGS)); } subSubtleLogDebugSubtle("Finish\n"); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/event.c0000644000175000017500000017150411770332063017574 0ustar formorerformorer /** * @package subtle * * @file Event functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/event.c,v 3202 2012/04/14 14:30:51 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include #include #include "subtle.h" #ifdef HAVE_SYS_INOTIFY_H #define BUFLEN (sizeof(struct inotify_event)) #endif /* HAVE_SYS_INOTIFY_H */ #ifdef HAVE_X11_EXTENSIONS_XRANDR_H #include #endif /* HAVE_X11_EXTENSIONS_XRANDR_H */ /* Globals */ struct pollfd *watches = NULL; XClientMessageEvent *queue = NULL; int nwatches = 0, nqueue = 0; /* EventUntag {{{ */ static void EventUntag(SubClient *c, int id) { int i, tag; /* Shift bits */ for(i = id; i < subtle->tags->ndata - 1; i++) { tag = (1L << (i + 1)); if(c->tags & (1L << (i + 2))) ///< Next bit c->tags |= tag; else c->tags &= ~tag; } /* EWMH: Tags */ if(c->flags & SUB_TYPE_CLIENT) { subEwmhSetCardinals(c->win, SUB_EWMH_SUBTLE_CLIENT_TAGS, (long *)&c->tags, 1); } } /* }}} */ /* EventFindSublet {{{ */ static SubPanel * EventFindSublet(int id) { int i = 0, j = 0, idx = 0; /* Find sublet in panels */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); for(j = 0; j < s->panels->ndata; j++) { SubPanel *p = PANEL(s->panels->data[j]); if(p->flags & SUB_PANEL_SUBLET && !(p->flags & SUB_PANEL_COPY) && idx++ == id) return p; } } return NULL; } /* }}} */ /* EventQueuePush {{{ */ static void EventQueuePush(XClientMessageEvent *ev, long type) { /* Since we are dealing with race conditions we need to cache * client messages when a client/view/tag isn't ready yet */ queue = (XClientMessageEvent *)subSharedMemoryRealloc(queue, (nqueue + 1) * sizeof(XClientMessageEvent)); queue[nqueue] = *ev; queue[nqueue].data.l[2] = type; nqueue++; subSubtleLogDebugEvents("Queue push: id=%ld, tags=%ld, type=%ld\n", ev->data.l[0], ev->data.l[1], type); } /* }}} */ /* EventQueuePop {{{ */ static void EventQueuePop(long value, long type) { /* Check queue */ if(queue) { int i; /* Check queued events */ for(i = 0; i < nqueue; i++) { /* Check event type matches */ if(queue[i].data.l[2] == type) { int j; subSubtleLogDebugEvents("Queue pop: id=%ld, tags=%ld, type=%ld\n", queue[i].data.l[0], queue[i].data.l[1], queue[i].data.l[2]); /* Update window id or array index and put back */ queue[i].data.l[0] = value; XPutBackEvent(subtle->dpy, (XEvent *)&queue[i]); /* Remove element from queue */ for(j = i; j < nqueue - 1; j++) queue[j] = queue[j + 1]; nqueue--; queue = (XClientMessageEvent *)subSharedMemoryRealloc(queue, nqueue * sizeof(XClientMessageEvent)); i--; } } } } /* }}} */ /* EventMatch {{{ */ static int EventMatch(int type, XRectangle *origin, XRectangle *test) { int cx_origin = 0, cx_test = 0, cy_origin = 0, cy_test = 0, dx = 0, dy = 0; /* This check is complicated and consists of three parts: * 1) Calculate window center positions * 2) Check if x/y values decrease in given direction * 3) Check if a corner of one of the rects is close enough to * a side of the other rect */ /* Calculate window centers */ cx_origin = origin->x + (origin->width / 2); cx_test = test->x + (test->width / 2); cy_origin = origin->y + (origin->height / 2); cy_test = test->y + (test->height / 2); /* Check geometries */ if((((SUB_GRAB_DIRECTION_LEFT == type && cx_test <= cx_origin) || (SUB_GRAB_DIRECTION_RIGHT == type && cx_test >= cx_origin)) && ((cy_test >= origin->y && cy_test <= origin->y + origin->height) || (cy_origin >= test->y && cy_origin <= test->y + test->height))) || (((SUB_GRAB_DIRECTION_UP == type && cy_test <= cy_origin) || (SUB_GRAB_DIRECTION_DOWN == type && cy_test >= cy_origin)) && ((cx_test >= origin->x && cx_test <= origin->x + origin->width) || (cx_origin >= test->x && cx_origin <= test->x + test->width)))) { /* Euclidean distance */ dx = abs(cx_origin - cx_test); dy = abs(cy_origin - cy_test); /* Zero distance means same dimensions - highest distance */ if(0 == dx && 0 == dy) dx = dy = 1L << 15; } else { /* No match - highest distance too */ dx = 1L << 15; dy = 1L << 15; } return dx + dy; } /* }}} */ /* Events */ /* EventColormap {{{ */ static void EventColormap(XColormapEvent *ev) { SubClient *c = (SubClient *)subSubtleFind(ev->window, 1); if(c && ev->new) { c->cmap = ev->colormap; XInstallColormap(subtle->dpy, c->cmap); } subSubtleLogDebugEvents("Colormap: win=%#lx\n", ev->window); } /* }}} */ /* EventConfigure {{{ */ static void EventConfigure(XConfigureEvent *ev) { /* Ckeck window */ if(ROOT == ev->window) { int sw = 0, sh = 0; #ifdef HAVE_X11_EXTENSIONS_XRANDR_H /* Update RandR config */ if(subtle->flags & SUB_SUBTLE_XRANDR) XRRUpdateConfiguration((XEvent *)ev); #endif /* HAVE_X11_EXTENSIONS_XRANDR_H */ /* Fetch screen geometry after update */ sw = DisplayWidth(subtle->dpy, DefaultScreen(subtle->dpy)); sh = DisplayHeight(subtle->dpy, DefaultScreen(subtle->dpy)); /* Skip event if screen size didn't change */ if(subtle->width == sw && subtle->height == sh) return; /* Reload screens */ subArrayClear(subtle->sublets, False); subArrayClear(subtle->screens, True); subScreenInit(); subRubyReloadConfig(); subScreenResize(); /* Update size */ subtle->width = sw; subtle->height = sh; printf("Updated screens\n"); } subSubtleLogDebugEvents("Configure: win=%#lx\n", ev->window); } /* }}} */ /* EventConfigureRequest {{{ */ static void EventConfigureRequest(XConfigureRequestEvent *ev) { SubClient *c = NULL; /* Complicated request! (see ICCCM 4.1.5) * No change -> Synthetic ConfigureNotify * Move/restack -> Synthetic + real ConfigureNotify * Resize -> Real ConfigureNotify */ /* Check window */ if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) { /* Check flags if the request is important */ if(!(c->flags & SUB_CLIENT_MODE_FULL) && (subtle->flags & SUB_SUBTLE_RESIZE || c->flags & (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_RESIZE))) { SubScreen *s = SCREEN(subtle->screens->data[c->screenid]); /* Handle request values and check if they make sense * Some clients outsmart us and center their toplevel windows */ if(ev->value_mask & CWX) c->geom.x = ev->x < s->geom.x ? s->geom.x + ev->x : ev->x; if(ev->value_mask & CWY) c->geom.y = ev->y < s->geom.y ? s->geom.y + ev->y : ev->y; if(ev->value_mask & CWWidth) c->geom.width = ev->width; if(ev->value_mask & CWHeight) c->geom.height = ev->height; subClientResize(c, &(s->geom), False); /* Send synthetic configure notify */ if(!(ev->value_mask & (CWX|CWY|CWWidth|CWHeight)) || ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))) subClientConfigure(c); /* Send real configure notify */ if(ev->value_mask & (CWX|CWY|CWWidth|CWHeight)) XMoveResizeWindow(subtle->dpy, c->win, c->geom.x, c->geom.y, c->geom.width, c->geom.height); } else subClientConfigure(c); } else ///< Unmanaged windows { XWindowChanges wc; wc.x = ev->x; wc.y = ev->y; wc.width = ev->width; wc.height = ev->height; wc.border_width = 0; wc.sibling = ev->above; wc.stack_mode = ev->detail; XConfigureWindow(subtle->dpy, ev->window, ev->value_mask, &wc); } subSubtleLogDebugEvents("ConfigureRequest: win=%#lx\n", ev->window); } /* }}} */ /* EventCrossing {{{ */ static void EventCrossing(XCrossingEvent *ev) { SubClient *c = NULL; SubScreen *s = NULL; /* Handle both crossing events */ switch(ev->type) { case EnterNotify: if((c = CLIENT(subSubtleFind(ev->window, CLIENTID))) && ALIVE(c)) ///< Client { if(!(subtle->flags & SUB_SUBTLE_FOCUS_CLICK)) subClientFocus(c, False); } else if((s = SCREEN(subSubtleFind(ev->window, SCREENID)))) ///< Screen panels { subPanelAction(s->panels, SUB_PANEL_OVER, ev->x, ev->y, -1, s->panel2 == ev->window); } break; case LeaveNotify: if((s = SCREEN(subSubtleFind(ev->window, SCREENID)))) ///< Screen panels { subPanelAction(s->panels, SUB_PANEL_OUT, ev->x, ev->y, -1, s->panel2 == ev->window); } } subSubtleLogDebugEvents("Enter: win=%#lx\n", ev->window); } /* }}} */ /* EventDestroy {{{ */ static void EventDestroy(XDestroyWindowEvent *ev) { SubClient *c = NULL; SubTray *t = NULL; /* Check if we know this window */ if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) ///< Client { int sid = (subtle->windows.focus[0] == c->win ? c->screenid : -1); ///< Save /* Kill client */ subArrayRemove(subtle->clients, (void *)c); subClientKill(c); subClientPublish(False); subScreenConfigure(); subScreenUpdate(); subScreenRender(); /* Update focus if necessary */ if((c = subClientNext(sid, False))) subClientFocus(c, True); } else if((t = TRAY(subSubtleFind(ev->event, TRAYID)))) ///< Tray { int focus = (subtle->windows.focus[0] == ev->window); ///< Save /* Kill tray */ subArrayRemove(subtle->trays, (void *)t); subTrayKill(t); subTrayUpdate(); subTrayPublish(); subScreenUpdate(); subScreenRender(); /* Update focus if necessary */ if(focus && (c = subClientNext(0, False))) subClientFocus(c, True); } else { int i; /* Check if window is client leader */ for(i = 0; i < subtle->clients->ndata; i++) { c = CLIENT(subtle->clients->data[i]); /* Mark all windows with leader as dead */ if(c->leader == ev->window) c->flags |= SUB_CLIENT_DEAD; } } subSubtleLogDebugEvents("Destroy: win=%#lx\n", ev->window); } /* }}} */ /* EventExpose {{{ */ static void EventExpose(XExposeEvent *ev) { if(0 == ev->count) subScreenRender(); ///< Render once subSubtleLogDebugEvents("Expose: win=%#lx\n", ev->window); } /* }}} */ /* EventFocus {{{ */ static void EventFocus(XFocusChangeEvent *ev) { /* Check window has focus or focus is caused by grabs */ if(ev->window == subtle->windows.focus[0] || NotifyGrab == ev->mode || NotifyUngrab == ev->mode) return; subSubtleLogDebugEvents("Focus: win=%#lx, mode=%d\n", ev->window, ev->mode); } /* }}} */ /* EventGrab {{{ */ static void EventGrab(XEvent *ev) { int x = 0, y = 0; unsigned int chain = False; SubGrab *g = NULL; SubClient *c = NULL; SubScreen *s = NULL; KeySym sym = None; /* Distinct types {{{ */ switch(ev->type) { case ButtonPress: if((s = SCREEN(subSubtleFind(ev->xbutton.window, SCREENID)))) ///< Screen panels { subPanelAction(s->panels, SUB_PANEL_DOWN, ev->xbutton.x, ev->xbutton.y, ev->xbutton.button, s->panel2 == ev->xbutton.window); return; } else if(subtle->flags & SUB_SUBTLE_FOCUS_CLICK && (c = CLIENT(subSubtleFind(ev->xbutton.window, CLIENTID)))) ///< Client { if(ALIVE(c)) subClientFocus(c, False); return; } g = subGrabFind((XK_Pointer_Button1 + ev->xbutton.button), ev->xbutton.state); ///< Build button number x = ev->xbutton.x; y = ev->xbutton.y; subSubtleLogDebugEvents("Grab: type=mouse, win=%#lx\n", ev->xbutton.window); break; case KeyPress: g = subGrabFind(ev->xkey.keycode, ev->xkey.state); x = ev->xkey.x; y = ev->xkey.y; subSubtleLogDebugEvents("Grab: type=key, keycode=%d, state=%d\n", ev->xkey.keycode, ev->xkey.state); break; } /* }}} */ /* Find grab */ /* Check chain end {{{ */ if(subtle->keychain) { /* Check if key is just a modifier */ if(!g) { sym = XLookupKeysym(&(ev->xkey), 0); if(IsModifierKey(sym)) return; } /* Check if grab belongs to current chain */ if(g && subtle->keychain->keys) chain = (-1 != subArrayIndex(subtle->keychain->keys, (void *)g)); /* Break chain on end or invalid link */ if(!chain || (g && !g->keys)) { /* Update panel if in use */ if(subtle->panels.keychain.keychain) { free(subtle->panels.keychain.keychain->keys); subtle->panels.keychain.keychain->keys = NULL; subtle->panels.keychain.keychain->len = 0; } subtle->keychain = NULL; subScreenUpdate(); subScreenRender(); /* Restore binds */ subGrabUnset(ROOT); printf("DEBUG %s:%d\n", __FILE__, __LINE__); subGrabSet(ROOT, SUB_GRAB_KEY); if(!chain) return; } } /* }}} */ /* Handle grab */ if(g) { FLAGS flag = g->flags & ~(SUB_TYPE_GRAB|SUB_GRAB_KEY|SUB_GRAB_MOUSE| SUB_GRAB_CHAIN_START|SUB_GRAB_CHAIN_LINK|SUB_GRAB_CHAIN_END); ///< Clear mask /* Handle key chains {{{ */ if(g->flags & SUB_GRAB_CHAIN_START && g->keys) { /* Update keychain panel if in use {{{ */ if(subtle->panels.keychain.keychain) { char *key = NULL, buf[12] = { 0 }; int len = 0, pos = 0; /* Convert event to key */ sym = XLookupKeysym(&(ev->xkey), 0); key = XKeysymToString(sym); /* Append space before each key */ if(0 < subtle->panels.keychain.keychain->len) buf[pos++] = ' '; /* Translate states to string {{{ */ if(g->state & ShiftMask) pos += snprintf(buf + pos, sizeof(buf) - pos, "%s", "S-"); if(g->state & ControlMask) pos += snprintf(buf + pos, sizeof(buf) - pos, "%s", "C-"); if(g->state & Mod1Mask) pos += snprintf(buf + pos, sizeof(buf) - pos, "%s", "A-"); if(g->state & Mod3Mask) pos += snprintf(buf + pos, sizeof(buf) - pos, "%s", "M-"); if(g->state & Mod4Mask) pos += snprintf(buf + pos, sizeof(buf) - pos, "%s", "W-"); if(g->state & Mod5Mask) pos += snprintf(buf + pos, sizeof(buf) - pos, "%s", "G-"); /* }}} */ /* Assemble chain string */ len += strlen(buf) + strlen(key); subtle->panels.keychain.keychain->keys = subSharedMemoryRealloc( subtle->panels.keychain.keychain->keys, (len + subtle->panels.keychain.keychain->len + 1) * sizeof(char)); snprintf(subtle->panels.keychain.keychain->keys + subtle->panels.keychain.keychain->len, subtle->panels.keychain.keychain->len + len, "%s%s", buf, key); subtle->panels.keychain.keychain->len += len; subScreenUpdate(); subScreenRender(); } /* }}} */ /* Keep chain position */ subtle->keychain = g; /* Bind any keys to exit chain on invalid link */ subGrabUnset(ROOT); XGrabKey(subtle->dpy, AnyKey, AnyModifier, ROOT, True, GrabModeAsync, GrabModeAsync); return; } /* }}} */ /* Select action */ switch(flag) { case SUB_GRAB_SPAWN: /* {{{ */ if(g->data.string) subSharedSpawn(g->data.string); break; /* }}} */ case SUB_GRAB_PROC: /* {{{ */ subRubyCall(SUB_CALL_HOOKS, g->data.num, subSubtleFind(subtle->windows.focus[0], CLIENTID)); break; /* }}} */ case SUB_GRAB_WINDOW_MOVE: case SUB_GRAB_WINDOW_RESIZE: /* {{{ */ /* Prevent resize of fixed and move/resize of fullscreen windows */ if((c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID))) && !(c->flags & SUB_CLIENT_MODE_FULL) && !(SUB_GRAB_WINDOW_RESIZE == flag && c->flags & SUB_CLIENT_MODE_FIXED)) { /* Set floating when necessary */ if(!(c->flags & SUB_CLIENT_MODE_FLOAT)) { subClientToggle(c, SUB_CLIENT_MODE_FLOAT, True); subScreenUpdate(); subScreenRender(); } /* Translate flags */ if(SUB_GRAB_WINDOW_MOVE == flag) flag = SUB_DRAG_MOVE; else if(SUB_GRAB_WINDOW_RESIZE == flag) flag = SUB_DRAG_RESIZE; subClientDrag(c, flag, (int)g->data.num); } break; /* }}} */ case SUB_GRAB_WINDOW_TOGGLE: /* {{{ */ if((c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID)))) { subClientToggle(c, g->data.num, True); /* Update screen and focus */ if(VISIBLE(c) || SUB_CLIENT_MODE_STICK == g->data.num) { subScreenConfigure(); /* Find next and focus */ if(!VISIBLE(c) && (c = subClientNext(c->screenid, False))) subClientFocus(c, True); subScreenUpdate(); subScreenRender(); } } break; /* }}} */ case SUB_GRAB_WINDOW_STACK: /* {{{ */ if((c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID))) && !(c->flags & SUB_CLIENT_TYPE_DESKTOP) && VISIBLE(c)) subClientRestack(c, g->data.num); break; /* }}} */ case SUB_GRAB_WINDOW_SELECT: /* {{{ */ { SubClient *found = NULL; /* Check if a window is currently focussed or just select next*/ if((c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID)))) { int i, j, match = (1L << 30), distance = 0; /* Iterate once to find a client with smallest distance */ for(i = 0; i < subtle->clients->ndata; i++) { SubClient *k = CLIENT(subtle->clients->data[i]); /* Check if both clients are different and visible */ if(c != k && (subtle->visible_tags & k->tags || k->flags & SUB_CLIENT_MODE_STICK)) { /* Substract stack position index to get top window */ distance = EventMatch(g->data.num, &c->geom, &k->geom) - i; /* Substract history stack position index */ for(j = 1; j < HISTORYSIZE; j++) { if(subtle->windows.focus[j] == k->win) { distance -= (HISTORYSIZE - j); break; } } /* Finally compare distance */ if(match > distance) { match = distance; found = k; } } } } else found = subClientNext(-1, True); if(found) subClientFocus(found, True); } break; /* }}} */ case SUB_GRAB_WINDOW_GRAVITY: /* {{{ */ if((c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID))) && !(c->flags & SUB_CLIENT_MODE_FIXED)) { int i, id = -1, cid = 0, fid = (int)g->data.string[0] - GRAVITYSTRLIMIT, size = strlen(g->data.string); /* Remove float/fullscreen mode */ if(c->flags & (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_FULL)) { subClientToggle(c, c->flags & (SUB_CLIENT_MODE_FLOAT|SUB_CLIENT_MODE_FULL), True); subScreenUpdate(); subScreenRender(); c->gravityid = -1; ///< Reset } /* Select next gravity */ for(i = 0; -1 == id && i < size; i++) { cid = (int)g->data.string[i] - GRAVITYSTRLIMIT; /* Toggle gravity */ if(c->gravityid == cid) { /* Select first or next id */ if(i == size - 1) id = fid; else id = (int)g->data.string[i + 1] - GRAVITYSTRLIMIT; } } if(-1 == id) id = fid; ///< Fallback /* Sanity check */ if(0 <= id && id < subtle->gravities->ndata) { subClientArrange(c, id, c->screenid); subClientRestack(c, SUB_CLIENT_RESTACK_UP); /* Warp pointer */ if(!(subtle->flags & SUB_SUBTLE_SKIP_WARP)) subClientWarp(c); /* Hook: Tile */ subHookCall(SUB_HOOK_TILE, NULL); } } break; /* }}} */ case SUB_GRAB_WINDOW_KILL: /* {{{ */ if((c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID)))) subClientClose(c); break; /* }}} */ case SUB_GRAB_VIEW_FOCUS: case SUB_GRAB_VIEW_SWAP: /* {{{ */ if(g->data.num < subtle->views->ndata) { int sid = -1; /* Find screen: Prefer screen of current window */ if(subtle->flags & SUB_SUBTLE_SKIP_WARP && (c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID))) && VISIBLE(c)) sid = c->screenid; else subScreenFind(x, y, &sid); /* Focus or swap */ subViewFocus(VIEW(subtle->views->data[g->data.num]), sid, (SUB_GRAB_VIEW_SWAP == flag), True); } break; /* }}} */ case SUB_GRAB_VIEW_SELECT: /* {{{ */ { int vid = 0, sid = 0; /* Find screen: Prefer screen of current window */ if(subtle->flags & SUB_SUBTLE_SKIP_WARP && (c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID))) && VISIBLE(c)) { s = SCREEN(subArrayGet(subtle->screens, c->screenid)); sid = c->screenid; } else s = subScreenFind(x, y, &sid); /* Select view */ if(SUB_VIEW_NEXT == g->data.num) { /* Cycle if necessary */ if(s->viewid < (subtle->views->ndata - 1)) vid = s->viewid + 1; } else if(SUB_VIEW_PREV == g->data.num) { /* Cycle if necessary */ if(0 < s->viewid) vid = s->viewid - 1; else vid = subtle->views->ndata - 1; } subViewFocus(subtle->views->data[vid], sid, False, True); } break; /* }}} */ case SUB_GRAB_SCREEN_JUMP: /* {{{ */ if(g->data.num < subtle->screens->ndata) subScreenWarp(SCREEN(subtle->screens->data[g->data.num])); break; /* }}} */ case SUB_GRAB_SUBTLE_RELOAD: /* {{{ */ if(subtle) subtle->flags |= SUB_SUBTLE_RELOAD; break; /* }}} */ case SUB_GRAB_SUBTLE_QUIT: /* {{{ */ if(subtle) subtle->flags &= ~SUB_SUBTLE_RUN; break; /* }}} */ case SUB_GRAB_SUBTLE_RESTART: /* {{{ */ if(subtle) { subtle->flags &= ~SUB_SUBTLE_RUN; subtle->flags |= SUB_SUBTLE_RESTART; } break; /* }}} */ default: subSubtleLogWarn("Cannot find grab action!\n"); } } } /* }}} */ /* EventMap {{{ */ static void EventMap(XMapEvent *ev) { SubTray *t = NULL; /* Check if we know the window */ if((t = TRAY(subSubtleFind(ev->window, TRAYID)))) ///< Tray { t->flags &= ~SUB_TRAY_DEAD; subTrayUpdate(); subScreenUpdate(); subScreenRender(); } subSubtleLogDebugEvents("Map: win=%#lx\n", ev->window); } /* }}} */ /* EventMapping {{{ */ static void EventMapping(XMappingEvent *ev) { XRefreshKeyboardMapping(ev); /* Update grabs */ if(MappingKeyboard == ev->request) { subGrabUnset(ROOT); subGrabSet(ROOT, SUB_GRAB_KEY); } subSubtleLogDebugEvents("Map: win=%#lx\n", ev->window); } /* }}} */ /* EventMapRequest {{{ */ static void EventMapRequest(XMapRequestEvent *ev) { SubClient *c = NULL; /* Check if we know the window */ if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) { c->flags &= ~SUB_CLIENT_DEAD; c->flags |= SUB_CLIENT_ARRANGE; subScreenConfigure(); subScreenUpdate(); subScreenRender(); } else if((c = subClientNew(ev->window))) { subArrayPush(subtle->clients, (void *)c); subClientRestack(c, SUB_CLIENT_RESTACK_UP); subScreenConfigure(); subScreenUpdate(); subScreenRender(); EventQueuePop(ev->window, SUB_TYPE_CLIENT); /* Hook: Create */ subHookCall((SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_CREATE), (void *)c); } subSubtleLogDebugEvents("MapRequest: win=%#lx\n", ev->window); } /* }}} */ /* EventMessage {{{ */ static void EventMessage(XClientMessageEvent *ev) { SubClient *c = NULL; SubTray *r = NULL; /* Messages for root window {{{ */ if(ROOT == ev->window) { SubPanel *p = NULL; SubTag *t = NULL; SubView *v = NULL; SubGravity *g = NULL; SubScreen *s = NULL; switch(subEwmhFind(ev->message_type)) { /* ICCCM */ case SUB_EWMH_NET_CURRENT_DESKTOP: /* {{{ */ /* Switchs views of screen */ if(0 <= ev->data.l[0] && ev->data.l[0] < subtle->views->ndata) { int sid = 0; /* Focus view of specified or current screen */ if(0 > ev->data.l[2] || ev->data.l[2] >= subtle->screens->ndata) { /* Find screen: Prefer screen of current window */ if(subtle->flags & SUB_SUBTLE_SKIP_WARP && (c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID))) && VISIBLE(c)) sid = c->screenid; else subScreenCurrent(&sid); } else sid = ev->data.l[2]; subViewFocus(subtle->views->data[ev->data.l[0]], sid, True, True); } break; /* }}} */ case SUB_EWMH_NET_ACTIVE_WINDOW: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->data.l[0], CLIENTID)))) { if(!(VISIBLE(c))) ///< Client is on current view? { int i; /* Find matching view */ for(i = 0; i < subtle->views->ndata; i++) { if(c && (VIEW(subtle->views->data[i])->tags & c->tags || c->flags & SUB_CLIENT_MODE_STICK)) { subViewFocus(VIEW(subtle->views->data[i]), c->screenid, False, True); break; } } } subClientFocus(c, True); } else if((r = TRAY(subSubtleFind(ev->data.l[0], TRAYID)))) { XSetInputFocus(subtle->dpy, r->win, RevertToPointerRoot, CurrentTime); } break; /* }}} */ case SUB_EWMH_NET_RESTACK_WINDOW: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->data.l[1], CLIENTID)))) subClientRestack(c, Above == ev->data.l[2] ? SUB_CLIENT_RESTACK_UP : SUB_CLIENT_RESTACK_DOWN); break; /* }}} */ /* subtle */ case SUB_EWMH_SUBTLE_CLIENT_TAGS: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->data.l[0], CLIENTID)))) { int i, flags = 0, tags = 0; /* Select only new tags */ tags = (c->tags ^ (int)ev->data.l[1]) & (int)ev->data.l[1]; /* Remove highlight of tagless, urgent client */ if(0 == tags && c->flags & SUB_CLIENT_MODE_URGENT) subtle->urgent_tags &= ~c->tags; /* Update tags and assign properties */ for(i = 0; i < 31; i++) if(tags & (1L << (i + 1))) subClientTag(c, i, &flags); subClientToggle(c, flags, True); ///< Toggle flags c->tags = (int)ev->data.l[1]; ///< Write all tags /* EWMH: Tags */ subEwmhSetCardinals(c->win, SUB_EWMH_SUBTLE_CLIENT_TAGS, (long *)&c->tags, 1); subScreenConfigure(); /* Check visibility of focus window after updating tags * and reactivate grabs if necessary */ if(subtle->windows.focus[0] == c->win && !VISIBLE(c)) { c = subClientNext(c->screenid, False); if(c) subClientFocus(c, True); } subScreenUpdate(); subScreenRender(); } else EventQueuePush(ev, SUB_TYPE_CLIENT); break; /* }}} */ case SUB_EWMH_SUBTLE_CLIENT_RETAG: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->data.l[0], CLIENTID)))) { int flags = 0; c->tags = 0; ///> Reset tags subClientRetag(c, &flags); subClientToggle(c, (~c->flags & flags), True); ///< Toggle flags if(VISIBLE(c) || flags & SUB_CLIENT_MODE_FULL) { subScreenConfigure(); subScreenUpdate(); subScreenRender(); } } break; /* }}} */ case SUB_EWMH_SUBTLE_CLIENT_GRAVITY: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->data.l[0], CLIENTID))) && ((g = GRAVITY(subArrayGet(subtle->gravities, (int)ev->data.l[1]))))) { /* Set gravity for specified view */ if((v = VIEW(subArrayGet(subtle->views, (int)ev->data.l[2])))) { c->gravities[(int)ev->data.l[2]] = (int)ev->data.l[1]; if(subtle->visible_views & (1L << ((int)ev->data.l[2] + 1))) { subClientArrange(c, c->gravities[(int)ev->data.l[2]], c->screenid); XRaiseWindow(subtle->dpy, c->win); /* Warp pointer */ if(!(subtle->flags & SUB_SUBTLE_SKIP_WARP)) subClientWarp(c); /* Hook: Tile */ subHookCall(SUB_HOOK_TILE, NULL); } } else if(VISIBLE(c)) { subClientArrange(c, (int)ev->data.l[1], c->screenid); XRaiseWindow(subtle->dpy, c->win); /* Warp pointer */ if(!(subtle->flags & SUB_SUBTLE_SKIP_WARP)) subClientWarp(c); /* Hook: Tile */ subHookCall(SUB_HOOK_TILE, NULL); } } break; /* }}} */ case SUB_EWMH_SUBTLE_CLIENT_FLAGS: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->data.l[0], CLIENTID)))) { int flags = 0; /* Translate flags */ if(ev->data.l[1] & SUB_EWMH_FULL) flags |= SUB_CLIENT_MODE_FULL; if(ev->data.l[1] & SUB_EWMH_FLOAT) flags |= SUB_CLIENT_MODE_FLOAT; if(ev->data.l[1] & SUB_EWMH_STICK) flags |= SUB_CLIENT_MODE_STICK; if(ev->data.l[1] & SUB_EWMH_RESIZE) flags |= SUB_CLIENT_MODE_RESIZE; if(ev->data.l[1] & SUB_EWMH_URGENT) flags |= SUB_CLIENT_MODE_URGENT; if(ev->data.l[1] & SUB_EWMH_ZAPHOD) flags |= SUB_CLIENT_MODE_ZAPHOD; if(ev->data.l[1] & SUB_EWMH_FIXED) flags |= SUB_CLIENT_MODE_FIXED; if(ev->data.l[1] & SUB_EWMH_BORDERLESS) flags |= SUB_CLIENT_MODE_BORDERLESS; subClientToggle(c, flags, False); /* Configure and render when necessary */ if(VISIBLE(c) || flags & (SUB_CLIENT_MODE_FULL|SUB_CLIENT_MODE_URGENT)) { subScreenConfigure(); subScreenUpdate(); subScreenRender(); } } else EventQueuePush(ev, SUB_TYPE_CLIENT); break; /* }}} */ case SUB_EWMH_SUBTLE_GRAVITY_NEW: /* {{{ */ if(ev->data.b) { XRectangle geom = { 0 }; char buf[30] = { 0 }; sscanf(ev->data.b, "%hdx%hd+%hd+%hd#%s", &geom.x, &geom.y, &geom.width, &geom.height, buf); /* Add gravity */ g = subGravityNew(buf, &geom); subArrayPush(subtle->gravities, (void *)g); subGravityPublish(); } break; /* }}} */ case SUB_EWMH_SUBTLE_GRAVITY_FLAGS: /* {{{ */ if((g = GRAVITY(subArrayGet(subtle->gravities, (int)ev->data.l[0])))) { int i, flags = 0; /* Translate and update flags */ if(ev->data.l[1] & SUB_EWMH_HORZ) flags |= SUB_GRAVITY_HORZ; if(ev->data.l[1] & SUB_EWMH_VERT) flags |= SUB_GRAVITY_VERT; g->flags = (g->flags & SUB_TYPE_GRAVITY) | flags; /* Find clients with that gravity and mark them for arrange */ for(i = 0; i < subtle->clients->ndata; i++) { c = CLIENT(subtle->clients->data[i]); if(c->gravityid == ev->data.l[0]) c->flags |= SUB_CLIENT_ARRANGE; } subScreenConfigure(); subScreenUpdate(); subScreenRender(); } break; /* }}} */ case SUB_EWMH_SUBTLE_GRAVITY_KILL: /* {{{ */ if((g = GRAVITY(subArrayGet(subtle->gravities, (int)ev->data.l[0])))) { int i; /* Check clients if gravity is in use */ for(i = 0; i < subtle->clients->ndata; i++) { if((c = CLIENT(subtle->clients->data[i])) && c->gravityid == ev->data.l[0]) { subClientArrange(c, 0, -1); ///< Fallback to first gravity XRaiseWindow(subtle->dpy, c->win); /* Warp pointer */ if(!(subtle->flags & SUB_SUBTLE_SKIP_WARP)) subClientWarp(c); } } /* Finallly remove gravity */ subArrayRemove(subtle->gravities, (void *)g); subGravityKill(g); subGravityPublish(); } break; /* }}} */ case SUB_EWMH_SUBTLE_SCREEN_JUMP: /* {{{ */ if((s = SCREEN(subArrayGet(subtle->screens, (int)ev->data.l[0])))) { subScreenWarp(s); } break; /* }}} */ case SUB_EWMH_SUBTLE_SUBLET_DATA: /* {{{ */ if((p = EventFindSublet((int)ev->data.l[0])) && p->sublet->flags & SUB_SUBLET_DATA) { subRubyCall(SUB_CALL_DATA, p->sublet->instance, NULL); subScreenUpdate(); subScreenRender(); } break; /* }}} */ case SUB_EWMH_SUBTLE_SUBLET_STYLE: /* {{{ */ if(ev->data.b) { int subletid = 0; char name[30] = { 0 }; sscanf(ev->data.b, "%d#%s", &subletid, name); /* Find sublet and state */ if((p = EventFindSublet(subletid))) { int styleid = -1; subStyleFind(&subtle->styles.sublets, name, &styleid); p->sublet->styleid = -1 != styleid ? styleid : -1; subScreenUpdate(); subScreenRender(); } } break; /* }}} */ case SUB_EWMH_SUBTLE_SUBLET_FLAGS: /* {{{ */ if((p = EventFindSublet((int)ev->data.l[0]))) { /* Update visibility */ if(ev->data.l[1] & SUB_EWMH_VISIBLE && p->flags & SUB_PANEL_HIDDEN) { p->flags &= ~SUB_PANEL_HIDDEN; subScreenUpdate(); subScreenRender(); } else if(ev->data.l[1] & SUB_EWMH_HIDDEN && !(p->flags & SUB_PANEL_HIDDEN)) { p->flags |= SUB_PANEL_HIDDEN; subScreenUpdate(); subScreenRender(); } } break; /* }}} */ case SUB_EWMH_SUBTLE_SUBLET_UPDATE: /* {{{ */ if((p = EventFindSublet((int)ev->data.l[0]))) { subRubyCall(SUB_CALL_RUN, p->sublet->instance, NULL); subScreenUpdate(); subScreenRender(); } break; /* }}} */ case SUB_EWMH_SUBTLE_SUBLET_KILL: /* {{{ */ if((p = EventFindSublet((int)ev->data.l[0]))) { subRubyUnloadSublet(p); subScreenUpdate(); subScreenRender(); } break; /* }}} */ case SUB_EWMH_SUBTLE_TAG_NEW: /* {{{ */ if(ev->data.b) { int duplicate = False; if((t = subTagNew(ev->data.b, &duplicate)) && !duplicate) { subArrayPush(subtle->tags, (void *)t); subTagPublish(); /* Hook: Create */ subHookCall((SUB_HOOK_TYPE_TAG|SUB_HOOK_ACTION_CREATE), (void *)t); } } break; /* }}} */ case SUB_EWMH_SUBTLE_TAG_KILL: /* {{{ */ if((t = TAG(subArrayGet(subtle->tags, (int)ev->data.l[0])))) { int i, reconf = False; /* Untag views */ for(i = 0; i < subtle->views->ndata; i++) ///< Views { v = VIEW(subtle->views->data[i]); reconf = v->tags & (1L << ((int)ev->data.l[0] + 1)); EventUntag(CLIENT(v), (int)ev->data.l[0]); } /* Untag clients */ for(i = 0; i < subtle->clients->ndata; i++) EventUntag(CLIENT(subtle->clients->data[i]), (int)ev->data.l[0]); /* Remove tag */ subArrayRemove(subtle->tags, (void *)t); subTagKill(t); subTagPublish(); subViewPublish(); if(reconf) subScreenConfigure(); } break; /* }}} */ case SUB_EWMH_SUBTLE_VIEW_NEW: /* {{{ */ if(ev->data.b && (v = subViewNew(ev->data.b, NULL))) { subArrayPush(subtle->views, (void *)v); subClientDimension(-1); ///< Grow subViewPublish(); subScreenUpdate(); subScreenRender(); EventQueuePop(subtle->views->ndata - 1, SUB_TYPE_VIEW); /* Hook: Create */ subHookCall((SUB_HOOK_TYPE_VIEW|SUB_HOOK_ACTION_CREATE), (void *)v); } break; /* }}} */ case SUB_EWMH_SUBTLE_VIEW_TAGS: /* {{{ */ if((v = VIEW(subArrayGet(subtle->views, (int)ev->data.l[0])))) { v->tags = (int)ev->data.l[1]; ///< Action subViewPublish(); /* Reconfigure if view is visible */ if(subtle->visible_views & (1L << (ev->data.l[0] + 1))) subScreenConfigure(); } else EventQueuePush(ev, SUB_TYPE_VIEW); break; /* }}} */ case SUB_EWMH_SUBTLE_VIEW_STYLE: /* {{{ */ if(ev->data.b) { int view_id = 0; char name[30] = { 0 }; sscanf(ev->data.b, "%d#%s", &view_id, name); /* Find sublet and state */ if((v = VIEW(subArrayGet(subtle->views, view_id)))) { int style_id = -1; subStyleFind(&subtle->styles.views, name, &style_id); v->styleid = -1 != style_id ? style_id : -1; subScreenUpdate(); subScreenRender(); } } break; /* }}} */ case SUB_EWMH_SUBTLE_VIEW_KILL: /* {{{ */ if((v = VIEW(subArrayGet(subtle->views, (int)ev->data.l[0])))) { int visible = !!(subtle->visible_views & (1L << (ev->data.l[0] + 1))); subArrayRemove(subtle->views, (void *)v); subClientDimension((int)ev->data.l[0]); ///< Shrink subViewKill(v); subViewPublish(); subScreenUpdate(); subScreenRender(); if(visible) subViewFocus(VIEW(subtle->views->data[0]), -1, False, True); } break; /* }}} */ case SUB_EWMH_SUBTLE_RENDER: /* {{{ */ subScreenRender(); break; /* }}} */ case SUB_EWMH_SUBTLE_RELOAD: /* {{{ */ if(subtle) subtle->flags |= SUB_SUBTLE_RELOAD; break; /* }}} */ case SUB_EWMH_SUBTLE_RESTART: /* {{{ */ if(subtle) { subtle->flags &= ~SUB_SUBTLE_RUN; subtle->flags |= SUB_SUBTLE_RESTART; } break; /* }}} */ case SUB_EWMH_SUBTLE_QUIT: /* {{{ */ if(subtle) subtle->flags &= ~SUB_SUBTLE_RUN; break; /* }}} */ default: break; } } /* }}} */ /* Messages for tray window {{{ */ else if(ev->window == subtle->windows.tray) { switch(subEwmhFind(ev->message_type)) { case SUB_EWMH_NET_SYSTEM_TRAY_OPCODE: /* {{{ */ switch(ev->data.l[1]) { case XEMBED_EMBEDDED_NOTIFY: /* {{{ */ if(!(r = TRAY(subSubtleFind(ev->window, TRAYID)))) { if((r = subTrayNew(ev->data.l[2]))) { subArrayPush(subtle->trays, (void *)r); subTrayPublish(); subTrayUpdate(); subScreenUpdate(); subScreenRender(); } } break; /* }}} */ case XEMBED_REQUEST_FOCUS: /* {{{ */ subEwmhMessage(r->win, SUB_EWMH_XEMBED, 0xFFFFFF, CurrentTime, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); break; /* }}} */ } break; /* }}} */ default: break; } } /* }}} */ /* Messages for tray windows {{{ */ else if((r = TRAY(subSubtleFind(ev->window, TRAYID)))) ///< Tray { switch(subEwmhFind(ev->message_type)) { case SUB_EWMH_NET_CLOSE_WINDOW: /* {{{ */ subTrayClose(r); break; /* }}} */ default: break; } } /* }}} */ /* Messages for client windows {{{ */ else if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) { DEAD(c); switch(subEwmhFind(ev->message_type)) { case SUB_EWMH_NET_WM_STATE: /* {{{ */ { int flags = 0; /* Translate properties */ subEwmhTranslateWMState(ev->data.l[1], &flags); subEwmhTranslateWMState(ev->data.l[2], &flags); /* Since we always toggle we need to be careful */ switch(ev->data.l[0]) { case _NET_WM_STATE_ADD: flags = (~c->flags & flags); break; case _NET_WM_STATE_REMOVE: flags = (c->flags & flags); break; case _NET_WM_STATE_TOGGLE: break; } subClientToggle(c, flags, True); /* Update screen and focus */ if(VISIBLE(c) || flags & SUB_CLIENT_MODE_STICK) { subScreenConfigure(); if(!VISIBLE(c)) { c = subClientNext(c->screenid, False); if(c) subClientFocus(c, True); } subScreenUpdate(); subScreenRender(); } } break; /* }}} */ case SUB_EWMH_NET_CLOSE_WINDOW: /* {{{ */ subClientClose(c); break; /* }}} */ case SUB_EWMH_NET_MOVERESIZE_WINDOW: /* {{{ */ { SubScreen *s = SCREEN(subtle->screens->data[c->screenid]); if(!(c->flags & SUB_CLIENT_MODE_FLOAT)) subClientToggle(c, SUB_CLIENT_MODE_FLOAT, True); c->geom.x = ev->data.l[1]; c->geom.y = ev->data.l[2]; c->geom.width = ev->data.l[3]; c->geom.height = ev->data.l[4]; subClientResize(c, &(s->geom), True); XMoveResizeWindow(subtle->dpy, c->win, c->geom.x, c->geom.y, c->geom.width, c->geom.height); if(VISIBLE(c)) { subScreenUpdate(); subScreenRender(); } } break; /* }}} */ default: break; } } /* }}} */ #ifdef DEBUG { char *name = XGetAtomName(subtle->dpy, ev->message_type); subSubtleLogDebugEvents("ClientMessage: name=%s, type=%ld," "format=%d, win=%#lx\n", name ? name : "n/a", ev->message_type, ev->format, ev->window); subSubtleLogDebugEvents("ClientMessage: [0]=%#lx, [1]=%#lx," "[2]=%#lx, [3]=%#lx, [4]=%#lx\n", ev->data.l[0], ev->data.l[1], ev->data.l[2], ev->data.l[3], ev->data.l[4]); if(name) XFree(name); } #endif } /* }}} */ /* EventProperty {{{ */ static void EventProperty(XPropertyEvent *ev) { SubClient *c = NULL; SubTray *t = NULL; int id = subEwmhFind(ev->atom); if(XA_WM_NAME == ev->atom) id = SUB_EWMH_WM_NAME; /* Supported properties */ switch(id) { case SUB_EWMH_WM_NAME: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) { if(c->name) free(c->name); subSharedPropertyName(subtle->dpy, c->win, &c->name, c->klass); if(subtle->windows.focus[0] == c->win) { subScreenUpdate(); subScreenRender(); } } break; /* }}} */ case SUB_EWMH_WM_NORMAL_HINTS: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) { int flags = 0; subClientSetSizeHints(c, &flags); subClientToggle(c, (~c->flags & flags), True); ///< Only enable if(VISIBLE(c)) { subScreenUpdate(); subScreenRender(); } } else if((t = TRAY(subSubtleFind(ev->window, TRAYID)))) { subTrayConfigure(t); subTrayUpdate(); subScreenUpdate(); subScreenRender(); } break; /* }}} */ case SUB_EWMH_WM_HINTS: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) { int flags = 0; /* Check changes */ subClientSetWMHints(c, &flags); subClientToggle(c, (~c->flags & flags), True); /* Update and render when necessary */ if(VISIBLE(c) || flags & SUB_CLIENT_MODE_URGENT) { subScreenUpdate(); subScreenRender(); } } break; /* }}} */ case SUB_EWMH_NET_WM_STRUT: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) { subClientSetStrut(c); subScreenUpdate(); subSubtleLogDebug("Hints: Updated strut hints\n"); } break; /* }}} */ case SUB_EWMH_MOTIF_WM_HINTS: /* {{{ */ if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) subClientSetMWMHints(c); break; /* }}} */ case SUB_EWMH_XEMBED_INFO: /* {{{ */ if((t = TRAY(subSubtleFind(ev->window, TRAYID)))) { subTraySetState(t); subTrayUpdate(); subScreenUpdate(); subScreenRender(); } break; /* }}} */ } #ifdef DEBUG { char *name = XGetAtomName(subtle->dpy, ev->atom); subSubtleLogDebugEvents("Property: name=%s, type=%ld, win=%#lx\n", name ? name : "n/a", ev->atom, ev->window); if(name) XFree(name); } #endif } /* }}} */ /* EventSelft on heection {{{ */ void EventSelection(XSelectionClearEvent *ev) { /* Handle selection clear events */ if(ev->window == subtle->windows.tray) ///< Tray selection { subtle->flags &= ~SUB_SUBTLE_TRAY; subTrayDeselect(); } else if(ev->window == subtle->windows.support) ///< Session selection { subSubtleLogWarn("Leaving the field\n"); subtle->flags &= ~SUB_SUBTLE_RUN; ///< Exit } subSubtleLogDebugEvents("SelectionClear: win=%#lx, tray=%#lx, support=%#lx\n", ev->window, subtle->windows.tray, subtle->windows.support); } /* }}} */ /* EventUnmap {{{ */ static void EventUnmap(XUnmapEvent *ev) { SubClient *c = NULL; SubTray *t = NULL; /* Check if we know this window */ if((c = CLIENT(subSubtleFind(ev->window, CLIENTID)))) { int sid = (subtle->windows.focus[0] == c->win ? c->screenid : -1); ///< Save /* Set withdrawn state (see ICCCM 4.1.4) */ subEwmhSetWMState(c->win, WithdrawnState); /* Ignore our generated unmap events */ if(c->flags & SUB_CLIENT_UNMAP) { c->flags &= ~SUB_CLIENT_UNMAP; return; } /* Kill client */ subArrayRemove(subtle->clients, (void *)c); subClientKill(c); subClientPublish(False); subScreenConfigure(); subScreenUpdate(); subScreenRender(); /* Update focus if necessary */ if((c = subClientNext(sid, False))) subClientFocus(c, True); } else if((t = TRAY(subSubtleFind(ev->window, TRAYID)))) ///< Tray { int focus = (subtle->windows.focus[0] == ev->window); ///< Save /* Set withdrawn state (see ICCCM 4.1.4) */ subEwmhSetWMState(t->win, WithdrawnState); /* Ignore our own generated unmap events */ if(t->flags & SUB_TRAY_UNMAP) { t->flags &= ~SUB_TRAY_UNMAP; return; } /* Kill tray */ subArrayRemove(subtle->trays, (void *)t); subTrayKill(t); subTrayUpdate(); subTrayPublish(); subScreenUpdate(); subScreenRender(); /* Update focus if necessary */ if(focus && (c = subClientNext(0, False))) subClientFocus(c, True); } subSubtleLogDebugEvents("Unmap: win=%#lx\n", ev->window); } /* }}} */ /* Public */ /** subEventWatchAdd {{{ * @brief Add descriptor to watch list * @param[in] fd File descriptor **/ void subEventWatchAdd(int fd) { /* Add descriptor to list */ watches = (struct pollfd *)subSharedMemoryRealloc(watches, (nwatches + 1) * sizeof(struct pollfd)); watches[nwatches].fd = fd; watches[nwatches].events = POLLIN; watches[nwatches++].revents = 0; } /* }}} */ /** subEventWatchDel {{{ * @brief Del fd from watch list * @param[in] fd File descriptor **/ void subEventWatchDel(int fd) { int i, j; for(i = 0; i < nwatches; i++) { if(watches[i].fd == fd) { for(j = i; j < nwatches - 1; j++) watches[j] = watches[j + 1]; break; } } nwatches--; watches = (struct pollfd *)subSharedMemoryRealloc(watches, nwatches * sizeof(struct pollfd)); } /* }}} */ /** subEventLoop {{{ * @brief Event all X events **/ void subEventLoop(void) { int i, timeout = 1, nevents = 0; XEvent ev; time_t now; SubPanel *p = NULL; SubClient *c = NULL; #ifdef HAVE_SYS_INOTIFY_H char buf[BUFLEN]; #endif /* HAVE_SYS_INOTIFY_H */ /* Update screens and panels */ subScreenConfigure(); subScreenUpdate(); subScreenRender(); subPanelPublish(); /* Add watches */ subEventWatchAdd(ConnectionNumber(subtle->dpy)); #ifdef HAVE_SYS_INOTIFY_H subEventWatchAdd(subtle->notify); #endif /* HAVE_SYS_INOTIFY_H */ /* Set tray selection */ if(subtle->flags & SUB_SUBTLE_TRAY) subTraySelect(); subtle->flags |= SUB_SUBTLE_RUN; XSync(subtle->dpy, False); ///< Sync before going on /* Set grabs and focus first client if any */ subGrabSet(ROOT, SUB_GRAB_KEY); c = subClientNext(0, False); if(c) subClientFocus(c, True); /* Hook: Start */ subHookCall(SUB_HOOK_START, NULL); /* Start main loop */ while(subtle && subtle->flags & SUB_SUBTLE_RUN) { now = subSubtleTime(); /* Check if we need to reload */ if(subtle->flags & SUB_SUBTLE_RELOAD) { int tray = subtle->flags & SUB_SUBTLE_TRAY; subtle->flags &= ~SUB_SUBTLE_RELOAD; subRubyReloadConfig(); /* Update tray selection */ if(tray && !(subtle->flags & SUB_SUBTLE_TRAY)) subTrayDeselect(); else if(!tray && subtle->flags & SUB_SUBTLE_TRAY) subTraySelect(); } /* Data ready on any connection */ if(0 < (nevents = poll(watches, nwatches, timeout * 1000))) { for(i = 0; i < nwatches; i++) ///< Find descriptor { if(0 != watches[i].revents) { if(watches[i].fd == ConnectionNumber(subtle->dpy)) ///< X events {{{ { while(XPending(subtle->dpy)) ///< X events { XNextEvent(subtle->dpy, &ev); switch(ev.type) { case ColormapNotify: EventColormap(&ev.xcolormap); break; case ConfigureNotify: EventConfigure(&ev.xconfigure); break; case ConfigureRequest: EventConfigureRequest(&ev.xconfigurerequest); break; case EnterNotify: case LeaveNotify: EventCrossing(&ev.xcrossing); break; case DestroyNotify: EventDestroy(&ev.xdestroywindow); break; case Expose: EventExpose(&ev.xexpose); break; case FocusIn: EventFocus(&ev.xfocus); break; case ButtonPress: case KeyPress: EventGrab(&ev); break; case MapNotify: EventMap(&ev.xmap); break; case MappingNotify: EventMapping(&ev.xmapping); break; case MapRequest: EventMapRequest(&ev.xmaprequest); break; case ClientMessage: EventMessage(&ev.xclient); break; case PropertyNotify: EventProperty(&ev.xproperty); break; case SelectionClear: EventSelection(&ev.xselectionclear); break; case UnmapNotify: EventUnmap(&ev.xunmap); break; default: break; } } } /* }}} */ #ifdef HAVE_SYS_INOTIFY_H else if(watches[i].fd == subtle->notify) ///< Inotify {{{ { if(0 < read(subtle->notify, buf, BUFLEN)) ///< Inotify events { struct inotify_event *event = (struct inotify_event *)&buf[0]; /* Skip unwatch events */ if(event && IN_IGNORED != event->mask) { if((p = PANEL(subSubtleFind( subtle->windows.support, event->wd)))) { subRubyCall(SUB_CALL_WATCH, p->sublet->instance, NULL); subScreenUpdate(); subScreenRender(); } } } } /* }}} */ #endif /* HAVE_SYS_INOTIFY_H */ else ///< Socket {{{ { if((p = PANEL(subSubtleFind(subtle->windows.support, watches[i].fd)))) { subRubyCall(SUB_CALL_WATCH, p->sublet->instance, NULL); subScreenUpdate(); subScreenRender(); } } /* }}} */ } } } else if(0 == nevents) ///< Timeout waiting for data or error {{{ { if(0 < subtle->sublets->ndata) { p = PANEL(subtle->sublets->data[0]); /* Update all pending sublets */ while(p && p->sublet->flags & SUB_SUBLET_INTERVAL && p->sublet->time <= now) { subRubyCall(SUB_CALL_RUN, p->sublet->instance, NULL); /* This may change during run */ if(p->sublet->flags & SUB_SUBLET_INTERVAL) { p->sublet->time = now + p->sublet->interval; ///< Adjust seconds p->sublet->time -= p->sublet->time % p->sublet->interval; } subArraySort(subtle->sublets, subPanelCompare); } subScreenUpdate(); subScreenRender(); } } /* }}} */ /* Set new timeout */ if(0 < subtle->sublets->ndata) { p = PANEL(subtle->sublets->data[0]); timeout = p->sublet->flags & SUB_SUBLET_INTERVAL ? p->sublet->time - now : 60; if(0 >= timeout) timeout = 1; ///< Sanitize } else timeout = 60; } /* Drop tray selection */ if(subtle->flags & SUB_SUBTLE_TRAY) subTrayDeselect(); } /* }}} */ /** subEventFinish {{{ * @brief Finish event processing **/ void subEventFinish(void) { if(watches) free(watches); if(queue) free(queue); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/hook.c0000644000175000017500000000310211770332063017377 0ustar formorerformorer /** * @package subtle * * @file Hook functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/hook.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" /** subHookNew {{{ * @brief Create new hook * @param[in] type Type of hook * @param[in] proc Hook proc * @return Returns a new #SubHook or \p NULL **/ SubHook * subHookNew(int type, unsigned long proc) { SubHook *h = NULL; assert(proc); /* Create new hook */ h = HOOK(subSharedMemoryAlloc(1, sizeof(SubHook))); h->flags = (SUB_TYPE_HOOK|type); h->proc = proc; subSubtleLogDebugSubtle("new=hook, type=%d, proc=%ld\n", type, proc); return h; } /* }}} */ /** subHookCall {{{ * @brief Emit a hook * @param[in] type Type of hook * @param[in] data Hook data **/ void subHookCall(int type, void *data) { int i; /* Call matching hooks */ for(i = 0; i < subtle->hooks->ndata; i++) { SubHook *h = HOOK(subtle->hooks->data[i]); if((h->flags & ~SUB_TYPE_HOOK) == type) { subRubyCall(SUB_CALL_HOOKS, h->proc, data); subSubtleLogDebug("call=hook, type=%d, proc=%ld, data=%p\n", type, h->proc, data); } } } /* }}} */ /** subHookKill {{{ * @brief Kill a hook * @param[in] h A #SubHook **/ void subHookKill(SubHook *h) { assert(h); free(h); subSubtleLogDebugSubtle("kill=hook\n"); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/panel.c0000644000175000017500000005703211770332063017551 0ustar formorerformorer /** * @package subtle * * @file Panel functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/panel.c,v 3205 2012/05/22 21:15:36 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" /* PanelRect {{{ */ static void PanelRect(Drawable drawable, int x, int width, SubStyle *s) { int mw = s->margin.left + s->margin.right; int mh = s->margin.top + s->margin.bottom; /* Filling */ XSetForeground(subtle->dpy, subtle->gcs.draw, s->bg); XFillRectangle(subtle->dpy, drawable, subtle->gcs.draw, x + s->margin.left, s->margin.top, width - mw, subtle->ph - mh); /* Borders */ XSetForeground(subtle->dpy, subtle->gcs.draw, s->top); XFillRectangle(subtle->dpy, drawable, subtle->gcs.draw, x + s->margin.left, s->margin.top, width - mw, s->border.top); XSetForeground(subtle->dpy, subtle->gcs.draw, s->right); XFillRectangle(subtle->dpy, drawable, subtle->gcs.draw, x + width - s->border.right - s->margin.right, s->margin.top, s->border.right, subtle->ph - mh); XSetForeground(subtle->dpy, subtle->gcs.draw, s->bottom); XFillRectangle(subtle->dpy, drawable, subtle->gcs.draw, x + s->margin.left, subtle->ph - s->border.bottom - s->margin.bottom, width - mw, s->border.bottom); XSetForeground(subtle->dpy, subtle->gcs.draw, s->left); XFillRectangle(subtle->dpy, drawable, subtle->gcs.draw, x + s->margin.left, s->margin.top, s->border.left, subtle->ph - mh); } /* }}} */ /* PanelSeparator {{{ */ static void PanelSeparator(int x, SubStyle *s, Drawable drawable) { /* Set window background and border*/ PanelRect(drawable, x, s->separator->width, s); subSharedDrawString(subtle->dpy, subtle->gcs.draw, s->font, drawable, x + STYLE_LEFT((*s)), s->font->y + STYLE_TOP((*s)), s->fg, s->bg, s->separator->string, strlen(s->separator->string)); } /* }}} */ /* PanelClientModes {{{ */ void PanelClientModes(SubClient *c, char *buf, int *width) { int x = 0; /* Collect window modes */ if(c->flags & SUB_CLIENT_MODE_FULL) x += snprintf(buf + x, sizeof(buf), "%c", '+'); if(c->flags & SUB_CLIENT_MODE_FLOAT) x += snprintf(buf + x, sizeof(buf), "%c", '^'); if(c->flags & SUB_CLIENT_MODE_STICK) x += snprintf(buf + x, sizeof(buf), "%c", '*'); if(c->flags & SUB_CLIENT_MODE_RESIZE) x += snprintf(buf + x, sizeof(buf), "%c", '~'); if(c->flags & SUB_CLIENT_MODE_ZAPHOD) x += snprintf(buf + x, sizeof(buf), "%c", '='); if(c->flags & SUB_CLIENT_MODE_FIXED) x += snprintf(buf + x, sizeof(buf), "%c", '!'); *width = subSharedStringWidth(subtle->dpy, subtle->styles.title.font, buf, strlen(buf), NULL, NULL, True); } /* }}} */ /* PanelViewStyle {{{ */ static void PanelViewStyle(SubView *v, int idx, int focus, SubStyle *s) { /* Select style */ if(subtle->styles.views.styles) { SubStyle *style = NULL; subStyleReset(s, -1); /* Pick base style */ if(!(style = subArrayGet(subtle->styles.views.styles, v->styleid))) { if(subtle->styles.focus && focus) style = subtle->styles.focus; else if(subtle->styles.occupied && subtle->client_tags & v->tags) style = subtle->styles.occupied; } /* Merge base style or default */ subStyleMerge(s, !style ? &subtle->styles.views : style); /* Apply modifiers */ if(subtle->styles.urgent && subtle->urgent_tags & v->tags) subStyleMerge(s, subtle->styles.urgent); if(subtle->styles.visible) { if(subtle->visible_views & (1L << (idx + 1))) subStyleMerge(s, subtle->styles.visible); } } else subStyleMerge(s, &subtle->styles.views); } /* }}} */ /* PanelSubletStyle {{{ */ static SubStyle * PanelSubletStyle(SubPanel *p) { SubStyle *s = NULL; /* Pick sublet style */ if(subtle->styles.sublets.styles) s = subArrayGet(subtle->styles.sublets.styles, p->sublet->styleid); return s ? s : &subtle->styles.sublets; } /* }}} */ /* Public */ /** subPanelNew {{{ * @brief Create a new panel * @param[in] type Type of the panel * @return Returns a #SubPanel or \p NULL **/ SubPanel * subPanelNew(int type) { SubPanel *p = NULL; /* Create new panel */ p = PANEL(subSharedMemoryAlloc(1, sizeof(SubPanel))); p->flags = (SUB_TYPE_PANEL|type); /* Handle panel item type */ switch(p->flags & (SUB_PANEL_ICON|SUB_PANEL_SUBLET|SUB_PANEL_VIEWS)) { case SUB_PANEL_ICON: /* {{{ */ p->icon = ICON(subSharedMemoryAlloc(1, sizeof(SubIcon))); break; /* }}} */ case SUB_PANEL_SUBLET: /* {{{ */ p->sublet = SUBLET(subSharedMemoryAlloc(1, sizeof(SubSublet))); /* Sublet specific */ p->sublet->time = subSubtleTime(); p->sublet->text = subTextNew(); p->sublet->styleid = -1; break; /* }}} */ case SUB_PANEL_VIEWS: /* {{{ */ p->flags |= SUB_PANEL_DOWN; break; /* }}} */ } subSubtleLogDebugSubtle("New: type=%d\n", type); return p; } /* }}} */ /** subPanelUpdate {{{ * @brief Update panel * @param[in] p A #SubPanel **/ void subPanelUpdate(SubPanel *p) { assert(p); /* Handle panel item type */ switch(p->flags & (SUB_PANEL_ICON|SUB_PANEL_KEYCHAIN| SUB_PANEL_SUBLET|SUB_PANEL_TITLE|SUB_PANEL_VIEWS)) { case SUB_PANEL_ICON: /* {{{ */ p->width = p->icon->width + subtle->styles.separator.padding.left + subtle->styles.separator.padding.right + 4; break; /* }}} */ case SUB_PANEL_KEYCHAIN: /* {{{ */ p->width = 0; if(p->keychain && p->keychain->keys) { /* Font offset, panel border and padding */ p->width = subSharedStringWidth(subtle->dpy, subtle->styles.separator.font, p->keychain->keys, p->keychain->len, NULL, NULL, True) + subtle->styles.separator.padding.left + subtle->styles.separator.padding.right; } break; /* }}} */ case SUB_PANEL_SUBLET: /* {{{ */ { SubStyle *s = PanelSubletStyle(p); /* Ensure min width */ p->width = MAX(s->min, p->sublet->width); } break; /* }}} */ case SUB_PANEL_TITLE: /* {{{ */ p->width = subtle->styles.clients.min; if(0 < subtle->clients->ndata) { SubClient *c = NULL; /* Find focus window */ if((c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID)))) { assert(c); DEAD(c); /* Exclude desktop type windows */ if(!(c->flags & SUB_CLIENT_TYPE_DESKTOP)) { char buf[5] = { 0 }; int width = 0, len = strlen(c->name); PanelClientModes(c, buf, &width); /* Font offset, panel border and padding */ p->width = subSharedStringWidth(subtle->dpy, subtle->styles.title.font, c->name, /* Limit string length */ len > subtle->styles.clients.right ? subtle->styles.clients.right : len, NULL, NULL, True) + width + STYLE_WIDTH(subtle->styles.title); /* Ensure min width */ p->width = MAX(subtle->styles.clients.min, p->width); } } } break; /* }}} */ case SUB_PANEL_VIEWS: /* {{{ */ p->width = subtle->styles.views.min; if(0 < subtle->views->ndata) { int i; SubStyle s = { -1, .flags = SUB_TYPE_STYLE, .border = { -1 }, .padding = { -1 }, .margin = { -1 }}; /* Update for each view */ for(i = 0; i < subtle->views->ndata; i++) { SubView *v = VIEW(subtle->views->data[i]); /* Skip dynamic views */ if(v->flags & SUB_VIEW_DYNAMIC && !(subtle->client_tags & v->tags)) continue; PanelViewStyle(v, i, (p->screen->viewid == i), &s); /* Update view width */ if(v->flags & SUB_VIEW_ICON_ONLY) v->width = v->icon->width + STYLE_WIDTH((s)); else { v->width = subSharedStringWidth(subtle->dpy, s.font, v->name, strlen(v->name), NULL, NULL, True) + STYLE_WIDTH((s)) + (v->icon ? v->icon->width + 3 : 0); } /* Ensure panel min width */ p->width += MAX(s.min, v->width); } /* Add width of view separator if any */ if(subtle->styles.viewsep) { p->width += (subtle->views->ndata - 1) * subtle->styles.viewsep->separator->width; } } break; /* }}} */ } subSubtleLogDebugSubtle("Update\n"); } /* }}} */ /** subPanelRender {{{ * @brief Render panel * @param[in] p A #SubPanel * @param[in] drawable Drawable for renderer **/ void subPanelRender(SubPanel *p, Drawable drawable) { assert(p); /* Draw separator before panel */ if(p->flags & SUB_PANEL_SEPARATOR1 && subtle->styles.separator.separator) { PanelSeparator(p->x - subtle->styles.separator.separator->width, &subtle->styles.separator, drawable); } /* Handle panel item type */ switch(p->flags & (SUB_PANEL_ICON|SUB_PANEL_KEYCHAIN| SUB_PANEL_SUBLET|SUB_PANEL_TITLE|SUB_PANEL_VIEWS)) { case SUB_PANEL_ICON: /* {{{ */ { int y = 0, icony = 0; y = subtle->styles.separator.font->y + STYLE_TOP(subtle->styles.separator); icony = p->icon->height > y ? subtle->styles.separator.margin.top : y - p->icon->height; subSharedDrawIcon(subtle->dpy, subtle->gcs.draw, drawable, p->x + 2 + subtle->styles.separator.padding.left, icony, p->icon->width, p->icon->height, subtle->styles.sublets.fg, subtle->styles.sublets.bg, p->icon->pixmap, p->icon->bitmap); } break; /* }}} */ case SUB_PANEL_KEYCHAIN: /* {{{ */ if(p->keychain && p->keychain->keys) { subSharedDrawString(subtle->dpy, subtle->gcs.draw, subtle->styles.separator.font, drawable, p->x + STYLE_LEFT(subtle->styles.separator), subtle->styles.separator.font->y + STYLE_TOP(subtle->styles.separator), subtle->styles.title.fg, subtle->styles.title.bg, p->keychain->keys, strlen(p->keychain->keys)); } break; /* }}} */ case SUB_PANEL_SUBLET: /* {{{ */ { SubStyle *s = PanelSubletStyle(p); /* Set window background and border*/ PanelRect(drawable, p->x, p->width, s); /* Render text parts */ subTextRender(p->sublet->text, s->font, subtle->gcs.draw, drawable, p->x + STYLE_LEFT((*s)), s->font->y + STYLE_TOP((*s)), s->fg, s->icon, s->bg); } break; /* }}} */ case SUB_PANEL_TITLE: /* {{{ */ if(0 < subtle->clients->ndata) { SubClient *c = NULL; if((c = CLIENT(subSubtleFind(subtle->windows.focus[0], CLIENTID))) && !(c->flags & SUB_CLIENT_TYPE_DESKTOP) && VISIBLE(c)) { int x = 0, y = 0, width = 0, len = 0; char buf[5] = { 0 }; DEAD(c); PanelClientModes(c, buf, &width); /* Set window background and border*/ PanelRect(drawable, p->x, p->width, &subtle->styles.title); /* Draw modes and title */ len = strlen(c->name); x = p->x + STYLE_LEFT(subtle->styles.title); y = subtle->styles.title.font->y + STYLE_TOP(subtle->styles.title); subSharedDrawString(subtle->dpy, subtle->gcs.draw, subtle->styles.title.font, drawable, x, y, subtle->styles.title.fg, subtle->styles.title.bg, buf, strlen(buf)); subSharedDrawString(subtle->dpy, subtle->gcs.draw, subtle->styles.title.font, drawable, x + width, y, subtle->styles.title.fg, subtle->styles.title.bg, c->name, /* Limit string length */ len > subtle->styles.clients.right ? subtle->styles.clients.right : len); } } break; /* }}} */ case SUB_PANEL_VIEWS: /* {{{ */ if(0 < subtle->views->ndata) { int i, vx = p->x; SubStyle s = { -1, .flags = SUB_TYPE_STYLE, .border = { -1 }, .padding = { -1 }, .margin = { -1 }}; /* View buttons */ for(i = 0; i < subtle->views->ndata; i++) { int x = 0; SubView *v = VIEW(subtle->views->data[i]); /* Skip dynamic views */ if(v->flags & SUB_VIEW_DYNAMIC && !(subtle->client_tags & v->tags)) continue; PanelViewStyle(v, i, (p->screen->viewid == i), &s); /* Set window background and border*/ PanelRect(drawable, vx, v->width, &s); x += STYLE_LEFT((s)); /* Draw view icon and/or text */ if(v->flags & SUB_VIEW_ICON) { int y = 0, icony = 0; y = s.font->y + STYLE_TOP((s)); icony = v->icon->height > y ? s.margin.top : y - v->icon->height; subSharedDrawIcon(subtle->dpy, subtle->gcs.draw, drawable, vx + x, icony, v->icon->width, v->icon->height, s.icon, s.bg, v->icon->pixmap, v->icon->bitmap); } if(!(v->flags & SUB_VIEW_ICON_ONLY)) { if(v->flags & SUB_VIEW_ICON) x += v->icon->width + 3; subSharedDrawString(subtle->dpy, subtle->gcs.draw, s.font, drawable, vx + x, s.font->y + STYLE_TOP((s)), s.fg, s.bg, v->name, strlen(v->name)); } vx += v->width; /* Draw view separator if any */ if(subtle->styles.viewsep && i < subtle->views->ndata - 1) { PanelSeparator(vx, subtle->styles.viewsep, drawable); vx += subtle->styles.viewsep->separator->width; } } } break; /* }}} */ } /* Draw separator after panel */ if(p->flags & SUB_PANEL_SEPARATOR2 && subtle->styles.separator.separator) { SubStyle *s = p->flags & SUB_PANEL_SUBLET && subtle->styles.subletsep ? subtle->styles.subletsep : &subtle->styles.separator; PanelSeparator(p->x + p->width, s, drawable); } subSubtleLogDebugSubtle("Render\n"); } /* }}} */ /** subPanelCompare {{{ * @brief Compare two panels * @param[in] a A #SubPanel * @param[in] b A #SubPanel * @return Returns the result of the comparison of both panels * @retval -1 a is smaller * @retval 0 a and b are equal * @retval 1 a is greater **/ int subPanelCompare(const void *a, const void *b) { SubPanel *p1 = *(SubPanel **)a, *p2 = *(SubPanel **)b; assert(a && b); /* Include only interval sublets */ if(!(p1->sublet->flags & (SUB_SUBLET_INTERVAL))) return 1; if(!(p2->sublet->flags & (SUB_SUBLET_INTERVAL))) return -1; return p1->sublet->time < p2->sublet->time ? -1 : (p1->sublet->time == p2->sublet->time ? 0 : 1); } /* }}} */ /** subPanelAction {{{ * @brief Handle panel action based on type * @param[in] panels A #SubArray * @param[in] type Action type * @param[in] x Pointer X position * @param[in] y Pointer Y position * @param[in] button Pointer button * @param[in] bottom Whether bottom panel or not **/ void subPanelAction(SubArray *panels, int type, int x, int y, int button, int bottom) { int i; /* FIXME: In order to find the correct panel item we * need to check all of them in a O(n) fashion and * check if they belong to the top or bottom panel. * Adding some kind of map for the x values would * be nice. */ /* Check panel items */ for(i = 0; i < panels->ndata; i++) { SubPanel *p = PANEL(panels->data[i]); /* Check if x is in panel rect */ if(p->flags & type && x >= p->x && x <= p->x + p->width) { /* Check if action is for bottom panel */ if((bottom && !(p->flags & SUB_PANEL_BOTTOM)) || (!bottom && p->flags & SUB_PANEL_BOTTOM)) continue; /* Handle panel item type */ switch(p->flags & (SUB_PANEL_SUBLET|SUB_PANEL_VIEWS)) { case SUB_PANEL_SUBLET: /* {{{ */ /* Handle action type */ switch(type) { case SUB_PANEL_OUT: subRubyCall(SUB_CALL_OUT, p->sublet->instance, NULL); break; case SUB_PANEL_OVER: subRubyCall(SUB_CALL_OVER, p->sublet->instance, NULL); break; case SUB_PANEL_DOWN: { int args[3] = { x - p->x, y, button }; subRubyCall(SUB_CALL_DOWN, p->sublet->instance, (void *)&args); } break; } subScreenUpdate(); subScreenRender(); break; /* }}} */ case SUB_PANEL_VIEWS: /* {{{ */ { int j, vx = p->x; for(j = 0; j < subtle->views->ndata; j++) { SubView *v = VIEW(subtle->views->data[j]); /* Skip dynamic views */ if(v->flags & SUB_VIEW_DYNAMIC && !(subtle->client_tags & v->tags)) continue; /* Check if x is in view rect */ if(x >= vx && x <= vx + v->width) { /* FIXME */ int sid = subArrayIndex(subtle->screens, (void *)p->screen); subViewFocus(v, sid, True, False); break; } /* Add view separator width if any */ if(subtle->styles.viewsep) vx += v->width + subtle->styles.viewsep->separator->width; else vx += v->width; } } break; /* }}} */ } } } } /* }}} */ /** subPanelGeometry {{{ * @brief Get geometry of panel for given style * @param[in] p A #SubPanel * @param[in] s A #SubStyle * @param[inout] geom A #XRectangle **/ void subPanelGeometry(SubPanel *p, SubStyle *s, XRectangle *geom) { assert(p && s && geom); /* Calculate panel geometry without style values */ geom->x = p->x + STYLE_LEFT((*s)); geom->y = p->flags & SUB_PANEL_BOTTOM ? p->screen->geom.y + p->screen->geom.height - subtle->ph : subtle->ph; geom->width = 0 == p->width ? 1 : p->width - STYLE_WIDTH((*s)); geom->height = subtle->ph - STYLE_HEIGHT((*s)); } /* }}} */ /** subPanelPublish {{{ * @brief Publish panels **/ void subPanelPublish(void) { int i = 0, j = 0, idx = 0; char **sublets = NULL, buf[30] = { 0 }; XRectangle geom = { 0 }; /* Alloc space */ sublets = (char **)subSharedMemoryAlloc(subtle->sublets->ndata, sizeof(char *)); /* We need to publish sublets here, because we cannot rely * on the sublets array which is reordered on every update */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); if(s->panels) { for(j = 0; j < s->panels->ndata; j++) { SubPanel *p = PANEL(s->panels->data[j]); /* Include sublets, exclude shallow copies */ if(p->flags & SUB_PANEL_SUBLET && !(p->flags & SUB_PANEL_COPY)) { subPanelGeometry(p, PanelSubletStyle(p), &geom); /* Add gravity to list */ snprintf(buf, sizeof(buf), "%dx%d+%d+%d#%s", geom.x, geom.y, geom.width, geom.height, p->sublet->name); sublets[idx] = (char *)subSharedMemoryAlloc(strlen(buf) + 1, sizeof(char)); strncpy(sublets[idx++], buf, strlen(buf)); } } } } /* EWMH: Sublet list and geometries */ subSharedPropertySetStrings(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_SUBLET_LIST), sublets, subtle->sublets->ndata); /* Tidy up */ for(i = 0; i < subtle->sublets->ndata; i++) free(sublets[i]); subSubtleLogDebugSubtle("Publish: sublets=%d\n", subtle->sublets->ndata); XSync(subtle->dpy, False); ///< Sync all changes free(sublets); } /* }}} */ /** subPanelKill {{{ * @brief Kill a panel * @param[in] p A #SubPanel **/ void subPanelKill(SubPanel *p) { assert(p); /* Handle panel item type */ switch(p->flags & (SUB_PANEL_COPY|SUB_PANEL_ICON| SUB_PANEL_KEYCHAIN|SUB_PANEL_SUBLET|SUB_PANEL_TRAY)) { case SUB_PANEL_COPY: break; case SUB_PANEL_ICON: /* {{{ */ if(p->icon) free(p->icon); break; /* }}} */ case SUB_PANEL_KEYCHAIN: /* {{{ */ if(p->keychain) { if(p->keychain->keys) free(p->keychain->keys); free(p->keychain); p->keychain = NULL; p->screen = NULL; } return; /* }}} */ case SUB_PANEL_SUBLET: /* {{{ */ if(!(p->flags & SUB_PANEL_COPY)) { /* Call unload */ if(p->sublet->flags & SUB_SUBLET_UNLOAD) subRubyCall(SUB_CALL_UNLOAD, p->sublet->instance, NULL); subRubyRelease(p->sublet->instance); /* Remove socket watch */ if(p->sublet->flags & SUB_SUBLET_SOCKET) { XDeleteContext(subtle->dpy, subtle->windows.support, p->sublet->watch); subEventWatchDel(p->sublet->watch); } #ifdef HAVE_SYS_INOTIFY_H /* Remove inotify watch */ if(p->sublet->flags & SUB_SUBLET_INOTIFY) { XDeleteContext(subtle->dpy, subtle->windows.support, p->sublet->watch); inotify_rm_watch(subtle->notify, p->sublet->interval); } #endif /* HAVE_SYS_INOTIFY_H */ if(p->sublet->name) { printf("Unloaded sublet (%s)\n", p->sublet->name); free(p->sublet->name); } if(p->sublet->text) subTextKill(p->sublet->text); free(p->sublet); } break; /* }}} */ case SUB_PANEL_TRAY: /* {{{ */ /* Reparent and return to avoid beeing destroyed */ XReparentWindow(subtle->dpy, subtle->windows.tray, ROOT, 0, 0); p->screen = NULL; return; /* }}} */ } free(p); subSubtleLogDebugSubtle("Kill\n"); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/style.c0000644000175000017500000001667211770332063017617 0ustar formorerformorer /** * @package subtle * * @file Style functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/style.c,v 3205 2012/05/22 21:15:36 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" /* StyleInheritSides {{{ */ static void StyleInheritSides(SubSides *s1, SubSides *s2, int merge) { if(-1 == s1->top || (merge && -1 != s2->top)) s1->top = s2->top; if(-1 == s1->right || (merge && -1 != s2->right)) s1->right = s2->right; if(-1 == s1->bottom || (merge && -1 != s2->bottom)) s1->bottom = s2->bottom; if(-1 == s1->left || (merge && -1 != s2->left)) s1->left = s2->left; } /* }}} */ /* StyleInherit {{{ */ static void StyleInherit(SubStyle *s1, SubStyle *s2) { assert(s1 && s2); /* Inherit unset colors */ if(-1 == s1->fg) s1->fg = s2->fg; if(-1 == s1->bg) s1->bg = s2->bg; if(-1 == s1->icon) s1->icon = s2->icon; if(-1 == s1->top) s1->top = s2->top; if(-1 == s1->right) s1->right = s2->right; if(-1 == s1->bottom) s1->bottom = s2->bottom; if(-1 == s1->left) s1->left = s2->left; /* Inherit unset border, padding and margin */ StyleInheritSides(&s1->border, &s2->border, False); StyleInheritSides(&s1->padding, &s2->padding, False); StyleInheritSides(&s1->margin, &s2->margin, False); /* Inherit font */ if(NULL == s1->font) s1->font = s2->font; if(s1->font) { /* Check max height of style */ if(s1 != &subtle->styles.clients || s1 != &subtle->styles.subtle) { int height = STYLE_HEIGHT((*s1)) + s1->font->height; if(height > subtle->ph) subtle->ph = height; } /* Update separator width after font */ if(s1->separator) { s1->separator->width = subSharedStringWidth(subtle->dpy, s1->font, s1->separator->string, strlen(s1->separator->string), NULL, NULL, True); /* Add style width to calculate it only once */ if(0 < s1->separator->width) s1->separator->width += STYLE_WIDTH((*s1)); } } /* Check nested styles */ if(s1->styles) { int i; /* Inherit unset values from parent style */ for(i = 0; i < s1->styles->ndata; i++) { SubStyle *style = STYLE(s1->styles->data[i]); /* Don't inherit values, these styles are just * added to the selected view styles */ if(subtle->styles.urgent == style || subtle->styles.visible == style) continue; StyleInherit(style, s1); /* Sanitize icon */ if(-1 == style->icon) style->icon = style->fg; } } } /* }}} */ /* StyleFont {{{ */ static void StyleFont(SubStyle *s, const char *name) { /* Check if style exists and font is defined */ if(s && !s->font) { subSubtleLogError("Cannot find a font definition for style `%s'\n", name); \ subSubtleFinish(); exit(-1); \ } } /* }}} */ /* Public */ /** subStyleNew {{{ * @brief Create new style * @return Returns a new #SubStyle or \p NULL **/ SubStyle * subStyleNew(void) { SubStyle *s = NULL; /* Create new style */ s = STYLE(subSharedMemoryAlloc(1, sizeof(SubStyle))); s->flags |= SUB_TYPE_STYLE; /* Init style values */ subStyleReset(s, -1); subSubtleLogDebugSubtle("New\n"); return s; } /* }}} */ /** subStyleFind {{{ * @brief Find style * @param[in] s A #SubStyle * @param[in] name Name of style state * @param[inout] idx Index of found state * @return Returns found #SubStyle or \p NULL **/ SubStyle * subStyleFind(SubStyle *s, char *name, int *idx) { SubStyle *found = NULL; assert(s); if(s->styles && name) { int i; /* Check each state */ for(i = 0; i < s->styles->ndata; i++) { SubStyle *style = STYLE(s->styles->data[i]); /* Compare state name */ if(0 == strcmp(name, style->name)) { found = style; if(idx) *idx = i; break; } } } subSubtleLogDebugSubtle("Find\n"); return found; } /* }}} */ /** subStyleReset {{{ * Reset style values * @param[in] s A #SubStyle **/ void subStyleReset(SubStyle *s, int val) { assert(s); /* Set value */ s->fg = s->bg = s->top = s->right = s->bottom = s->left = val; s->border.top = s->border.right = s->border.bottom = s->border.left = val; s->padding.top = s->padding.right = s->padding.bottom = s->padding.left = val; s->margin.top = s->margin.right = s->margin.bottom = s->margin.left = val; /* Force value to prevent inheriting of 0 value from all */ s->icon = -1; /* Reset font */ if(s->flags & SUB_STYLE_FONT && s->font) { subSharedFontKill(subtle->dpy, s->font); s->flags &= ~SUB_STYLE_FONT; } s->font = NULL; /* Reset separator */ if(s->flags & SUB_STYLE_SEPARATOR && s->separator) { free(s->separator->string); free(s->separator); } s->separator = NULL; /* Remove states */ if(s->styles) subArrayKill(s->styles, True); s->styles = NULL; subSubtleLogDebugSubtle("Reset\n"); } /* }}} */ /** subStyleMerge {{{ * @brief Merge styles * @param[inout] s1 Style to assign values to * @param[in] s2 Style to copy values from **/ void subStyleMerge(SubStyle *s1, SubStyle *s2) { assert(s1 && s2); /* Merge set colors */ if(-1 != s2->fg) s1->fg = s2->fg; if(-1 != s2->bg) s1->bg = s2->bg; if(-1 != s2->icon) s1->icon = s2->icon; if(-1 != s2->top) s1->top = s2->top; if(-1 != s2->right) s1->right = s2->right; if(-1 != s2->bottom) s1->bottom = s2->bottom; if(-1 != s2->left) s1->left = s2->left; /* Merge font */ if(NULL != s2->font) s1->font = s2->font; /* Merge set border, padding and margin */ StyleInheritSides(&s1->border, &s2->border, True); StyleInheritSides(&s1->padding, &s2->padding, True); StyleInheritSides(&s1->margin, &s2->margin, True); subSubtleLogDebugSubtle("Merge\n"); } /* }}} */ /** subStyleKill {{{ * @brief Kill a style * @param[in] s A #SubStyle **/ void subStyleKill(SubStyle *s) { assert(s); /* Free font */ if(s->flags & SUB_STYLE_FONT && s->font) subSharedFontKill(subtle->dpy, s->font); /* Free separator */ if(s->flags & SUB_STYLE_SEPARATOR && s->separator) { free(s->separator->string); free(s->separator); } if(s->name) free(s->name); if(s->styles) subArrayKill(s->styles, True); free(s); subSubtleLogDebugSubtle("Kill\n"); } /* }}} */ /* All */ /** subStyleUpdate {{{ * Inherit style values from all **/ void subStyleUpdate(void) { /* Inherit styles */ StyleInherit(&subtle->styles.views, &subtle->styles.all); StyleInherit(&subtle->styles.title, &subtle->styles.all); StyleInherit(&subtle->styles.sublets, &subtle->styles.all); StyleInherit(&subtle->styles.separator, &subtle->styles.all); /* Check font */ StyleFont(&(subtle->styles.title), "title"); StyleFont(&(subtle->styles.separator), "separator"); StyleFont(&(subtle->styles.sublets), "sublets"); StyleFont(subtle->styles.occupied, "occupied"); StyleFont(subtle->styles.focus, "focus"); StyleFont(subtle->styles.viewsep, "view separator"); StyleFont(subtle->styles.subletsep, "sublet separator"); subSubtleLogDebugSubtle("Update\n"); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/text.c0000644000175000017500000001260211770332063017430 0ustar formorerformorer /** * @package subtle * * @file Text functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/text.c,v 3205 2012/05/22 21:15:36 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" /** subTextNew {{{ * @brief Create new text **/ SubText * subTextNew(void) { return TEXT(subSharedMemoryAlloc(1, sizeof(SubText))); } /* }}} */ /** subTextParse {{{ * @brief Parse a string and store it in given text * @param]inout] t A #SubText * @param[inout] f A #SubFont * @param[in] text Text to parse * @return Returns the length of the text in chars **/ int subTextParse(SubText *t, SubFont *f, char *text) { int i = 0, left = 0, right = 0; char *tok = NULL; long color = -1, pixmap = 0; SubTextItem *item = NULL; assert(f && t); t->width = 0; /* Split and iterate over tokens */ while((tok = strsep(&text, SEPARATOR))) { if('#' == *tok) color = strtol(tok + 1, NULL, 0); ///< Color else if('\0' != *tok) ///< Text or icon { /* Re-use items to save alloc cycles */ if(i < t->nitems && (item = ITEM(t->items[i]))) { if(!(item->flags & (SUB_TEXT_BITMAP|SUB_TEXT_PIXMAP)) && item->data.string) free(item->data.string); item->flags &= ~(SUB_TEXT_EMPTY|SUB_TEXT_BITMAP|SUB_TEXT_PIXMAP); } else if((item = ITEM(subSharedMemoryAlloc(1, sizeof(SubTextItem))))) { /* Add icon to array */ t->items = (SubTextItem **)subSharedMemoryRealloc(t->items, (t->nitems + 1) * sizeof(SubTextItem *)); t->items[(t->nitems)++] = item; } /* Get geometry of bitmap/pixmap */ if(('!' == *tok || '&' == *tok) && (pixmap = strtol(tok + 1, NULL, 0))) { XRectangle geometry = { 0 }; subSharedPropertyGeometry(subtle->dpy, pixmap, &geometry); item->flags |= ('!' == *tok ? SUB_TEXT_BITMAP : SUB_TEXT_PIXMAP); item->data.num = pixmap; item->width = geometry.width; item->height = geometry.height; /* Add spacing and check if icon is first */ t->width += item->width + (0 == i ? 3 : 6); item->color = color; } else ///< Ordinary text { item->data.string = strdup(tok); item->width = subSharedStringWidth(subtle->dpy, f, tok, strlen(tok), &left, &right, False); /* Remove left bearing from first text item */ t->width += item->width - (0 == i ? left : 0); item->color = color; } i++; } } /* Mark other items a clean */ for(; i < t->nitems; i++) ITEM(t->items[i])->flags |= SUB_TEXT_EMPTY; /* Fix spacing of last item */ if(item) { if(item->flags & (SUB_TEXT_BITMAP|SUB_TEXT_PIXMAP)) t->width -= 2; else { t->width -= right; item->width -= right; } } return t->width; } /* }}} */ /** subTextRender {{{ * @brief Render text on window at given position * @param[inout] t A #SubText * @param[inout] f A #SubFont * @param[in] gc A #GC * @param[in] win Window to draw on * @param[in] x X position * @param]in] y Y position * @param[in] fg Foreground color * @param[in] icon Icon color * @param[in] bg Background color **/ void subTextRender(SubText *t, SubFont *f, GC gc, Window win, int x, int y, long fg, long icon, long bg) { int i, width = x; assert(t); /* Render text items */ for(i = 0; i < t->nitems; i++) { SubTextItem *item = ITEM(t->items[i]); if(item->flags & SUB_TEXT_EMPTY) ///< Empty text { break; ///< Break loop } else if(item->flags & (SUB_TEXT_BITMAP|SUB_TEXT_PIXMAP)) ///< Icons { int icony = 0, dx = (0 == i) ? 0 : 3; ///< Add spacing when icon isn't first icony = item->height > f->height ? y - f->y - ((item->height - f->height) / 2): y - item->height; subSharedDrawIcon(subtle->dpy, gc, win, width + dx, icony, item->width, item->height, (-1 == item->color) ? icon : item->color, bg, (Pixmap)item->data.num, (item->flags & SUB_TEXT_BITMAP)); /* Add spacing when icon isn't last */ width += item->width + dx + (i != t->nitems - 1 ? 3 : 0); } else ///< Text { subSharedDrawString(subtle->dpy, gc, f, win, width, y, (-1 == item->color) ? fg : item->color, bg, item->data.string, strlen(item->data.string)); width += item->width; } } } /* }}} */ /** subTextKill {{{ * @brief Delete text * @param[in] t A #SubText **/ void subTextKill(SubText *t) { int i; assert(t); for(i = 0; i < t->nitems; i++) { SubTextItem *item = (SubTextItem *)t->items[i]; if(!(item->flags & (SUB_TEXT_BITMAP|SUB_TEXT_PIXMAP)) && item->data.string) free(item->data.string); free(t->items[i]); } free(t->items); free(t); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/view.c0000644000175000017500000001115711770332063017422 0ustar formorerformorer /** * @package subtle * * @file View functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/view.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" /** subViewNew {{{ * @brief Create a new view * @param[in] name Name of the view * @param[in] tags Tags for the view * @return Returns a #SubView or \p NULL **/ SubView * subViewNew(char *name, char *tags) { SubView *v = NULL; assert(name); /* Create new view */ v = VIEW(subSharedMemoryAlloc(1, sizeof(SubView))); v->flags = SUB_TYPE_VIEW; v->styleid = -1; v->name = strdup(name); /* Tags */ if(tags && strncmp("", tags, 1)) { int i; regex_t *preg = subSharedRegexNew(tags); for(i = 0; i < subtle->tags->ndata; i++) if(subSharedRegexMatch(preg, TAG(subtle->tags->data[i])->name)) v->tags |= (1L << (i + 1)); subSharedRegexKill(preg); } subSubtleLogDebugSubtle("New: name=%s\n", name); return v; } /* }}} */ /** subViewFocus {{{ * @brief Jump to view on screen or swap both * @param[in] v A #SubView * @param[in] screenid Screen id * @param[in] swap Whether to swap views * @param[in] focus Whether to focus next client **/ void subViewFocus(SubView *v, int screenid, int swap, int focus) { int vid = 0; SubScreen *s1 = NULL; SubClient *c = NULL; assert(v); /* Select screen and find vid */ s1 = SCREEN(subArrayGet(subtle->screens, screenid)); vid = subArrayIndex(subtle->views, (void *)v); /* Swap only makes sense with more than one screen */ if(swap && 1 < subtle->screens->ndata) { /* Check if view is visible on any screen */ if(subtle->visible_views & (1L << (vid + 1))) { int i; /* Find screen with view and swap */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s2 = SCREEN(subtle->screens->data[i]); if(s2->viewid == vid) { s2->viewid = s1->viewid; break; } } } } /* Set view and configure */ s1->viewid = vid; subScreenConfigure(); subScreenRender(); subScreenPublish(); /* Update focus */ if(focus) { /* Restore focus on view */ if(!((c = CLIENT(subSubtleFind(v->focus, CLIENTID))) && VISIBLETAGS(c, v->tags))) { c = subClientNext(screenid, False); v->focus = None; } if(c) subClientFocus(c, True); } /* Hook: Focus */ subHookCall((SUB_HOOK_TYPE_VIEW|SUB_HOOK_ACTION_FOCUS), (void *)v); subSubtleLogDebugSubtle("Focus: focus=%d\n", focus); } /* }}} */ /** SubViewKill {{{ * @brief Kill a view * @param[in] v A #SubView **/ void subViewKill(SubView *v) { assert(v); /* Hook: Kill */ subHookCall((SUB_HOOK_TYPE_VIEW|SUB_HOOK_ACTION_KILL), (void *)v); if(v->icon) free(v->icon); free(v->name); free(v); subSubtleLogDebugSubtle("Kill\n"); } /* }}} */ /* All */ /** subViewPublish {{{ * @brief Update EWMH infos **/ void subViewPublish(void) { int i; long vid = 0, *tags = NULL, *icons = NULL; char **names = NULL; if(0 < subtle->views->ndata) { tags = (long *)subSharedMemoryAlloc(subtle->views->ndata, sizeof(long)); icons = (long *)subSharedMemoryAlloc(subtle->views->ndata, sizeof(long)); names = (char **)subSharedMemoryAlloc(subtle->views->ndata, sizeof(char *)); for(i = 0; i < subtle->views->ndata; i++) { SubView *v = VIEW(subtle->views->data[i]); tags[i] = v->tags; icons[i] = v->icon ? v->icon->pixmap : -1; names[i] = v->name; } /* EWMH: Tags */ subEwmhSetCardinals(ROOT, SUB_EWMH_SUBTLE_VIEW_TAGS, tags, subtle->views->ndata); /* EWMH: Icons */ subEwmhSetCardinals(ROOT, SUB_EWMH_SUBTLE_VIEW_ICONS, icons, subtle->views->ndata); /* EWMH: Desktops */ subEwmhSetCardinals(ROOT, SUB_EWMH_NET_NUMBER_OF_DESKTOPS, (long *)&i, 1); subSharedPropertySetStrings(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_NET_DESKTOP_NAMES), names, subtle->views->ndata); /* EWMH: Current desktop */ subEwmhSetCardinals(ROOT, SUB_EWMH_NET_CURRENT_DESKTOP, &vid, 1); XSync(subtle->dpy, False); ///< Sync all changes free(tags); free(icons); free(names); } subSubtleLogDebugSubtle("Publish: views=%d\n", subtle->views->ndata); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/tray.c0000644000175000017500000001660611770332063017433 0ustar formorerformorer /** * @package subtle * * @file Tray functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/tray.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" /** subTrayNew {{{ * @brief Create new tray * @param[in] win Tray window * @return Returns a new #SubTray or \p NULL **/ SubTray * subTrayNew(Window win) { SubTray *t = NULL; int i, n = 0; Atom *protos = NULL; assert(win); /* Create new tray */ t = TRAY(subSharedMemoryAlloc(1, sizeof(SubTray))); t->flags = SUB_TYPE_TRAY; t->win = win; t->width = subtle->ph; ///< Default width /* Update tray properties */ subSharedPropertyName(subtle->dpy, win, &t->name, PKG_NAME); subEwmhSetWMState(t->win, WithdrawnState); XSelectInput(subtle->dpy, t->win, TRAYMASK); XReparentWindow(subtle->dpy, t->win, subtle->windows.tray, 0, 0); XAddToSaveSet(subtle->dpy, t->win); XSaveContext(subtle->dpy, t->win, TRAYID, (void *)t); /* Window manager protocols */ if(XGetWMProtocols(subtle->dpy, t->win, &protos, &n)) { for(i = 0; i < n; i++) { switch(subEwmhFind(protos[i])) { case SUB_EWMH_WM_DELETE_WINDOW: t->flags |= SUB_TRAY_CLOSE; break; default: break; } } XFree(protos); } /* Start embedding life cycle */ subEwmhMessage(t->win, SUB_EWMH_XEMBED, 0xFFFFFF, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0, subtle->windows.tray, 0); subSubtleLogDebugSubtle("New: name=%s, win=%#lx\n", t->name, win); return t; } /* }}} */ /** subTrayConfigure {{{ * @brief Configure tray * @param[in] t A #SubTray **/ void subTrayConfigure(SubTray *t) { long supplied = 0; XSizeHints *hints = NULL; assert(t); /* Size hints */ if(!(hints = XAllocSizeHints())) { subSubtleLogError("Cannot alloc memory. Exhausted?\n"); abort(); } XGetWMNormalHints(subtle->dpy, t->win, hints, &supplied); if(0 < supplied) { if(hints->flags & (USSize|PSize)) ///< User/program size t->width = MINMAX(hints->width, subtle->ph, 2 * subtle->ph); else if(hints->flags & PBaseSize) ///< Base size t->width = MINMAX(hints->base_width, subtle->ph, 2 * subtle->ph); else if(hints->flags & PMinSize) ///< Min size t->width = MINMAX(hints->min_width, subtle->ph, 2 * subtle->ph); } XFree(hints); subSubtleLogDebug("Configure: width=%d, supplied=%ld\n", t->width, supplied); } /* }}} */ /** subTrayUpdate {{{ * @brief Update tray window **/ void subTrayUpdate(void) { subtle->panels.tray.width = 0; ///< Reset width if(0 < subtle->trays->ndata) { int i; /* Resize every tray */ for(i = 0, subtle->panels.tray.width = 3; i < subtle->trays->ndata; i++) { SubTray *t = TRAY(subtle->trays->data[i]); if(t->flags & SUB_TRAY_DEAD) continue; XMapWindow(subtle->dpy, t->win); XMoveResizeWindow(subtle->dpy, t->win, subtle->panels.tray.width, 0, t->width, subtle->ph); subtle->panels.tray.width += t->width; } subtle->panels.tray.width += 3; ///< Add padding XMapRaised(subtle->dpy, subtle->windows.tray); XResizeWindow(subtle->dpy, subtle->windows.tray, subtle->panels.tray.width, subtle->ph); } else XUnmapWindow(subtle->dpy, subtle->windows.tray); } /* }}} */ /** subTraySetState {{{ * @brief Set window state and map/unmap accordingly * @param[in] t A #SubTray **/ void subTraySetState(SubTray *t) { long flags = 0; assert(t); /* Get xembed data */ if((flags = subEwmhGetXEmbedState(t->win))) { int opcode = 0; if(flags & XEMBED_MAPPED) ///< Map if wanted { opcode = XEMBED_WINDOW_ACTIVATE; XMapRaised(subtle->dpy, t->win); subEwmhSetWMState(t->win, NormalState); } else { t->flags |= SUB_TRAY_UNMAP; opcode = XEMBED_WINDOW_DEACTIVATE; XUnmapWindow(subtle->dpy, t->win); subEwmhSetWMState(t->win, WithdrawnState); } subEwmhMessage(t->win, SUB_EWMH_XEMBED, 0xFFFFFF, CurrentTime, opcode, 0, 0, 0); } } /* }}} */ /** subTraySelect {{{ * @brief Set tray selection owner for screen **/ void subTraySelect(void) { Atom selection = subEwmhGet(SUB_EWMH_NET_SYSTEM_TRAY_SELECTION); /* Tray selection */ XSetSelectionOwner(subtle->dpy, selection, subtle->windows.tray, CurrentTime); if(XGetSelectionOwner(subtle->dpy, selection) == subtle->windows.tray) { subSubtleLogDebug("Selection: type=%ld\n", selection); } else subSubtleLogError("Cannot get system tray selection\n"); /* Send manager info */ subEwmhMessage(ROOT, SUB_EWMH_MANAGER, 0xFFFFFF, CurrentTime, subEwmhGet(SUB_EWMH_NET_SYSTEM_TRAY_SELECTION), subtle->windows.tray, 0, 0); } /* }}} */ /** subTrayDeselect {{{ * @brief Unset tray selection owner for screen **/ void subTrayDeselect(void) { Atom selection = subEwmhGet(SUB_EWMH_NET_SYSTEM_TRAY_SELECTION); /* Tray selection */ if(XGetSelectionOwner(subtle->dpy, selection) == subtle->windows.tray) { XSetSelectionOwner(subtle->dpy, selection, None, CurrentTime); subSharedPropertyDelete(subtle->dpy, ROOT, selection); } } /* }}} */ /** subTrayClose {{{ * @brief Send tray delete message or just kill it * @param[in] t A #SubTray **/ void subTrayClose(SubTray *t) { assert(t); /* Honor window preferences (see ICCCM 4.1.2.7, 4.2.8.1) */ if(t->flags & SUB_TRAY_CLOSE) { subEwmhMessage(t->win, SUB_EWMH_WM_PROTOCOLS, NoEventMask, subEwmhGet(SUB_EWMH_WM_DELETE_WINDOW), CurrentTime, 0, 0, 0); } else { int focus = (subtle->windows.focus[0] == t->win); ///< Save /* Kill it manually */ XKillClient(subtle->dpy, t->win); subArrayRemove(subtle->trays, (void *)t); subTrayKill(t); subTrayPublish(); subTrayUpdate(); subScreenUpdate(); subScreenRender(); /* Update focus if necessary */ if(focus) { SubClient *c = subClientNext(0, False); if(c) subClientFocus(c, True); } } subSubtleLogDebugSubtle("Close\n"); } /* }}} */ /** subTrayKill {{{ * @brief Kill tray * @param[in] t A #SubTray **/ void subTrayKill(SubTray *t) { assert(t); /* Ignore further events and delete context */ XSelectInput(subtle->dpy, t->win, NoEventMask); XDeleteContext(subtle->dpy, t->win, TRAYID); /* Unembed tray icon following xembed specs */ XUnmapWindow(subtle->dpy, t->win); XReparentWindow(subtle->dpy, t->win, ROOT, 0, 0); XMapRaised(subtle->dpy, t->win); if(t->name) free(t->name); free(t); subSubtleLogDebugSubtle("Kill\n"); } /* }}} */ /* All */ /** subTrayPublish {{{ * @brief Publish trays **/ void subTrayPublish(void) { int i; Window *wins = (Window *)subSharedMemoryAlloc(subtle->trays->ndata, sizeof(Window)); for(i = 0; i < subtle->trays->ndata; i++) wins[i] = TRAY(subtle->trays->data[i])->win; /* EWMH: Client list and client list stacking */ subEwmhSetWindows(ROOT, SUB_EWMH_SUBTLE_TRAY_LIST, wins, subtle->trays->ndata); XSync(subtle->dpy, False); ///< Sync all changes free(wins); subSubtleLogDebugSubtle("Publish: trays=%d\n", subtle->trays->ndata); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/ruby.c0000644000175000017500000032315411770332063017434 0ustar formorerformorer /** * @package subtle * * @file Ruby functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/ruby.c,v 3223 2012/06/20 11:29:44 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include #include #include #include #include #include #include #include #include #include "subtle.h" #ifdef HAVE_WORDEXP_H #include #endif /* HAVE_WORDEXP_H */ /* Macros {{{ */ #define CHAR2SYM(name) ID2SYM(rb_intern(name)) #define SYM2CHAR(sym) rb_id2name(SYM2ID(sym)) /* }}} */ /* Globals {{{ */ static VALUE shelter = Qnil, mod = Qnil, config_sublets = Qnil; static VALUE config_instance = Qnil, config_methods = Qnil; /* }}} */ /* Typedef {{{ */ typedef struct rubysymbol_t { VALUE sym; int flags; } RubySymbols; typedef struct rubymethods_t { VALUE sym, real; int flags, arity; } RubyMethods; /* }}} */ /* RubyBacktrace {{{ */ static void RubyBacktrace(void) { VALUE lasterr = Qnil; /* Get last error */ if(!NIL_P(lasterr = rb_gv_get("$!"))) { int i; VALUE message = Qnil, klass = Qnil, backtrace = Qnil, entry = Qnil; /* Fetching backtrace data */ message = rb_obj_as_string(lasterr); klass = rb_class_path(CLASS_OF(lasterr)); backtrace = rb_funcall(lasterr, rb_intern("backtrace"), 0, NULL); /* Print error and backtrace */ subSubtleLogWarn("%s: %s\n", RSTRING_PTR(klass), RSTRING_PTR(message)); for(i = 0; Qnil != (entry = rb_ary_entry(backtrace, i)); ++i) printf("\tfrom %s\n", RSTRING_PTR(entry)); } } /* }}} */ /* RubyFilter {{{ */ static inline int #ifdef IS_OPENBSD RubyFilter(struct dirent *entry) #else RubyFilter(const struct dirent *entry) #endif { return !fnmatch("*.rb", entry->d_name, FNM_PATHNAME); } /* }}} */ /* RubyReceiver {{{ */ static int RubyReceiver(unsigned long instance, unsigned long meth) { VALUE receiver = Qnil; /* Check object instance */ if(rb_obj_is_instance_of(meth, rb_cMethod)) receiver = rb_funcall(meth, rb_intern("receiver"), 0, NULL); return receiver == instance; } /* }}} */ /* RubyFont {{{ */ static SubFont * RubyFont(const char *fontname) { SubFont *f = NULL; /* Load font */ if(!(f = subSharedFontNew(subtle->dpy, fontname))) { subSubtleLogWarn("Cannot load font `%s'\n", fontname); /* Load fallback font */ if(!(f = subSharedFontNew(subtle->dpy, DEFFONT))) { subSubtleLogError("Cannot load fallback font `%s'\n", DEFFONT); subSubtleFinish(); exit(-1); ///< Should never happen } } return f; } /* }}} */ /* Type converter */ /* RubySubtleToSubtlext {{{ */ static VALUE RubySubtleToSubtlext(void *data) { SubClient *c = NULL; VALUE object = Qnil; /* Convert subtle object to subtlext */ if((c = CLIENT(data))) { int id = 0; VALUE subtlext = Qnil, klass = Qnil; XSync(subtle->dpy, False); ///< Sync before going on subtlext = rb_const_get(rb_mKernel, rb_intern("Subtlext")); if(c->flags & SUB_TYPE_CLIENT) /* {{{ */ { int flags = 0; VALUE value = Qnil; /* Create client instance */ id = subArrayIndex(subtle->clients, (void *)c); klass = rb_const_get(subtlext, rb_intern("Client")); object = rb_funcall(klass, rb_intern("new"), 1, INT2FIX(id)); /* Translate flags */ subEwmhTranslateClientMode(c->flags, &flags); /* Set properties */ rb_iv_set(object, "@win", LONG2NUM(c->win)); rb_iv_set(object, "@flags", INT2FIX(flags)); rb_iv_set(object, "@tags", INT2FIX(c->tags)); rb_iv_set(object, "@name", rb_str_new2(c->name)); rb_iv_set(object, "@instance", rb_str_new2(c->instance)); rb_iv_set(object, "@klass", rb_str_new2(c->klass)); rb_iv_set(object, "@role", c->role ? rb_str_new2(c->role) : Qnil); /* Create and set geometry */ klass = rb_const_get(subtlext, rb_intern("Geometry")); value = rb_funcall(klass, rb_intern("new"), 4, INT2FIX(c->geom.x), INT2FIX(c->geom.y), INT2FIX(c->geom.width), INT2FIX(c->geom.height)); rb_iv_set(object, "@geometry", value); /* Create and set gravity if any */ if(-1 != c->gravityid) { SubGravity *g = GRAVITY(subArrayGet(subtle->gravities, c->gravityid)); klass = rb_const_get(subtlext, rb_intern("Gravity")); value = rb_funcall(klass, rb_intern("new"), 1, rb_str_new2(XrmQuarkToString(g->quark))); rb_funcall(value, rb_intern("geometry="), 4, INT2FIX(g->geom.x), INT2FIX(g->geom.y), INT2FIX(g->geom.width), INT2FIX(g->geom.height)); } else value = Qnil; rb_iv_set(object, "@gravity", value); } /* }}} */ else if(c->flags & SUB_TYPE_SCREEN) /* {{{ */ { SubScreen *s = SCREEN(c); VALUE klass_geom, geom = Qnil; /* Create tag instance */ id = subArrayIndex(subtle->screens, (void *)s); klass = rb_const_get(subtlext, rb_intern("Screen")); klass_geom = rb_const_get(subtlext, rb_intern("Geometry")); object = rb_funcall(klass, rb_intern("new"), 1, INT2FIX(id)); geom = rb_funcall(klass_geom, rb_intern("new"), 4, INT2FIX(s->geom.x), INT2FIX(s->geom.y), INT2FIX(s->geom.width), INT2FIX(s->geom.height)); /* Set properties */ rb_iv_set(object, "@geometry", geom); } /* }}} */ else if(c->flags & SUB_TYPE_TAG) /* {{{ */ { SubTag *t = TAG(c); /* Create tag instance */ id = subArrayIndex(subtle->tags, (void *)t); klass = rb_const_get(subtlext, rb_intern("Tag")); object = rb_funcall(klass, rb_intern("new"), 1, rb_str_new2(t->name)); /* Set properties */ rb_iv_set(object, "@id", INT2FIX(id)); } /* }}} */ else if(c->flags & SUB_TYPE_VIEW) /* {{{ */ { SubView *v = VIEW(c); /* Create view instance */ id = subArrayIndex(subtle->views, (void *)v); klass = rb_const_get(subtlext, rb_intern("View")); object = rb_funcall(klass, rb_intern("new"), 1, rb_str_new2(v->name)); /* Set properties */ rb_iv_set(object, "@id", INT2FIX(id)); rb_iv_set(object, "@tags", INT2FIX(v->tags)); } /* }}} */ } return object; } /* }}} */ /* RubyIconToIcon {{{ */ static void RubyIconToIcon(VALUE icon, SubIcon *i) { VALUE width = Qnil, height = Qnil, pixmap = Qnil, bitmap = Qnil; assert(i); /* Get properties */ width = rb_iv_get(icon, "@width"); height = rb_iv_get(icon, "@height"); pixmap = rb_iv_get(icon, "@pixmap"); bitmap = rb_funcall(icon, rb_intern("bitmap?"), 0, NULL); /* Update panel */ i->pixmap = NUM2LONG(pixmap); i->width = FIX2INT(width); i->height = FIX2INT(height); i->bitmap = (Qtrue == bitmap) ? True : False; } /* }}} */ /* RubySymbolToFlag {{{ */ static void RubySymbolToFlag(VALUE sym, int *flags) { /* Translate symbols to flags */ if(CHAR2SYM("name") == sym) (*flags) |= SUB_TAG_MATCH_NAME; else if(CHAR2SYM("instance") == sym) (*flags) |= SUB_TAG_MATCH_INSTANCE; else if(CHAR2SYM("class") == sym) (*flags) |= SUB_TAG_MATCH_CLASS; else if(CHAR2SYM("role") == sym) (*flags) |= SUB_TAG_MATCH_ROLE; else if(CHAR2SYM("type") == sym) (*flags) |= SUB_TAG_MATCH_TYPE; else if(CHAR2SYM("normal") == sym) (*flags) |= SUB_CLIENT_TYPE_NORMAL; else if(CHAR2SYM("desktop") == sym) (*flags) |= SUB_CLIENT_TYPE_DESKTOP; else if(CHAR2SYM("dock") == sym) (*flags) |= SUB_CLIENT_TYPE_DOCK; else if(CHAR2SYM("toolbar") == sym) (*flags) |= SUB_CLIENT_TYPE_TOOLBAR; else if(CHAR2SYM("splash") == sym) (*flags) |= SUB_CLIENT_TYPE_SPLASH; else if(CHAR2SYM("dialog") == sym) (*flags) |= SUB_CLIENT_TYPE_DIALOG; } /* }}} */ /* RubyArrayToArray {{{ */ static void RubyArrayToArray(VALUE ary, int *values, int len) { /* Check value type */ if(T_ARRAY == rb_type(ary)) { int i; VALUE value = Qnil; for(i = 0; i < len; i++) { /* Check and convert value type */ switch(rb_type(value = rb_ary_entry(ary, i))) { case T_FIXNUM: values[i] = FIX2INT(value); break; case T_FLOAT: values[i] = FIX2INT(rb_to_int(value)); break; default: values[i] = 0; break; } } } } /* }}} */ /* RubyArrayToGeometry {{{ */ static void RubyArrayToGeometry(VALUE ary, XRectangle *geometry) { int values[4] = { 0 }; RubyArrayToArray(ary, values, 4); /* Assign data to geometry */ geometry->x = values[0]; geometry->y = values[1]; geometry->width = values[2]; geometry->height = values[3]; } /* }}} */ /* RubyArrayToSides {{{ */ static void RubyArrayToSides(VALUE ary, SubSides *sides) { int values[4] = { 0 }; RubyArrayToArray(ary, values, 4); /* Assign data to sides CSS-alike: * 2 values => 1. top/bottom, 2. left/right * 3 values => 1. top, 2. left/right, 3. bottom * 4 values => 1. top, 2. right, 3. bottom, 4. left **/ switch((int)RARRAY_LEN(ary)) { case 2: sides->top = sides->bottom = values[0]; sides->right = sides->left = values[1]; break; case 3: sides->top = values[0]; sides->left = sides->right = values[1]; sides->bottom = values[2]; break; case 4: sides->top = values[0]; sides->right = values[1]; sides->bottom = values[2]; sides->left = values[3]; break; default: rb_raise(rb_eArgError, "Too many array values"); } } /* }}} */ /* RubyValueToGravity {{{ */ static int RubyValueToGravity(VALUE value) { int gravity = -1; /* Check value type */ switch(rb_type(value)) { case T_FIXNUM: gravity = FIX2INT(value); break; case T_SYMBOL: gravity = subGravityFind(SYM2CHAR(value), 0); break; } return 0 <= gravity && gravity < subtle->gravities->ndata ? gravity : -1; } /* }}} */ /* RubyValueToGravityString {{{ */ static void RubyValueToGravityString(VALUE value, char **string) { /* Check value type */ if(T_ARRAY == rb_type(value)) { int i = 0, j = 0, id = -1, size = 0; VALUE entry = Qnil; /* Create gravity string */ size = RARRAY_LEN(value); *string = (char *)subSharedMemoryAlloc(size + 1, sizeof(char)); /* Add gravities */ for(i = 0, j = 0; Qnil != (entry = rb_ary_entry(value, i)); i++) { /* We store gravity ids in a string to save a bit of memory */ if(-1 != (id = RubyValueToGravity(entry))) (*string)[j++] = id+ GRAVITYSTRLIMIT; else subSubtleLogWarn("Cannot find gravity `%s'\n", SYM2CHAR(entry)); } } } /* }}} */ /* RubyValueToIcon {{{ */ static VALUE RubyValueToIcon(VALUE value) { VALUE icon = Qnil, subtlext = Qnil, klass = Qnil; /* Fetch data */ subtlext = rb_const_get(rb_cObject, rb_intern("Subtlext")); klass = rb_const_get(subtlext, rb_intern("Icon")); /* Check icon */ switch(rb_type(value)) { case T_DATA: /* Check object instance */ if(rb_obj_is_instance_of(value, klass)) { icon = value; ///< Lazy eval rb_ary_push(shelter, icon); ///< Protect from GC } break; case T_STRING: /* Create new text icon */ icon = rb_funcall(klass, rb_intern("new"), 1, value); rb_ary_push(shelter, icon); ///< Protect from GC break; default: break; } return icon; } /* }}} */ /* RubyValueToHash {{{ */ static VALUE RubyValueToHash(VALUE value) { VALUE hash = Qnil; /* Check value type */ switch(rb_type(value)) { case T_HASH: hash = value; break; case T_NIL: break; ///< Ignore this case default: /* Convert to hash */ hash = rb_hash_new(); rb_hash_aset(hash, Qnil, value); } return hash; } /* }}} */ /* RubyHashToColor {{{ */ static void RubyHashToColor(VALUE hash, const char *key, long *col) { VALUE value = Qnil; /* Parse and set color if key found */ if(T_STRING == rb_type(value = rb_hash_lookup(hash, CHAR2SYM(key)))) *col = (long)subSharedParseColor(subtle->dpy, RSTRING_PTR(value)); } /* }}} */ /* RubyHashToInt {{{ */ static void RubyHashToInt(VALUE hash, const char *key, int *val) { VALUE value = Qnil; /* Parse and set color if key found */ if(FIXNUM_P(value = rb_hash_lookup(hash, CHAR2SYM(key)))) *val = FIX2INT(value); } /* }}} */ /* RubyHashToBorder {{{ */ static void RubyHashToBorder(VALUE hash, const char *key, long *col, int *bw) { VALUE value = Qnil; /* Parse and set color if key found */ switch(rb_type(value = rb_hash_lookup(hash, CHAR2SYM(key)))) { case T_ARRAY: { VALUE val = Qnil; if(T_STRING == rb_type(val = rb_ary_entry(value, 0))) *col = (long)subSharedParseColor(subtle->dpy, RSTRING_PTR(val)); if(FIXNUM_P(val = rb_ary_entry(value, 1))) *bw = FIX2INT(val); } break; case T_STRING: *col = (long)subSharedParseColor(subtle->dpy, RSTRING_PTR(value)); break; } } /* }}} */ /* RubyHashToSides {{{ */ static void RubyHashToSides(VALUE params, const char *name, SubSides *s) { VALUE value = Qnil; /* Get sides value */ if(T_ARRAY == rb_type(value = rb_hash_lookup(params, CHAR2SYM(name)))) ///< Two or more values RubyArrayToSides(value, s); else if(FIXNUM_P(value)) ///< Single value { s->top = s->right = s->bottom = s->left = FIX2INT(value); } else { char buf[15] = { 0 }; /* Painful string concat.. */ snprintf(buf, sizeof(buf), "%s_top", name); RubyHashToInt(params, buf, &s->top); snprintf(buf, sizeof(buf), "%s_right", name); RubyHashToInt(params, buf, &s->right); snprintf(buf, sizeof(buf), "%s_bottom", name); RubyHashToInt(params, buf, &s->bottom); snprintf(buf, sizeof(buf), "%s_left", name); RubyHashToInt(params, buf, &s->left); } } /* }}} */ /* Eval */ /* RubyEvalHook {{{ */ static void RubyEvalHook(VALUE event, VALUE proc) { int i; SubHook *h = NULL; RubySymbols hooks[] = { { CHAR2SYM("start"), SUB_HOOK_START }, { CHAR2SYM("exit"), SUB_HOOK_EXIT }, { CHAR2SYM("tile"), SUB_HOOK_TILE }, { CHAR2SYM("reload"), SUB_HOOK_RELOAD }, { CHAR2SYM("client_create"), (SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_CREATE) }, { CHAR2SYM("client_mode"), (SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_MODE) }, { CHAR2SYM("client_gravity"), (SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_GRAVITY) }, { CHAR2SYM("client_focus"), (SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_FOCUS) }, { CHAR2SYM("client_kill"), (SUB_HOOK_TYPE_CLIENT|SUB_HOOK_ACTION_KILL) }, { CHAR2SYM("tag_create"), (SUB_HOOK_TYPE_TAG|SUB_HOOK_ACTION_CREATE) }, { CHAR2SYM("tag_kill"), (SUB_HOOK_TYPE_TAG|SUB_HOOK_ACTION_KILL) }, { CHAR2SYM("view_create"), (SUB_HOOK_TYPE_VIEW|SUB_HOOK_ACTION_CREATE) }, { CHAR2SYM("view_focus"), (SUB_HOOK_TYPE_VIEW|SUB_HOOK_ACTION_FOCUS) }, { CHAR2SYM("view_kill"), (SUB_HOOK_TYPE_VIEW|SUB_HOOK_ACTION_KILL) } }; if(subtle->flags & SUB_SUBTLE_CHECK) return; ///< Skip on check /* Generic hooks */ for(i = 0; LENGTH(hooks) > i; i++) { if(hooks[i].sym == event) { /* Create new hook */ if((h = subHookNew(hooks[i].flags, proc))) { subArrayPush(subtle->hooks, (void *)h); rb_ary_push(shelter, proc); ///< Protect from GC } break; } } } /* }}} */ /* RubyEvalGrab {{{ */ static void RubyEvalGrab(VALUE keys, VALUE value) { int type = -1; SubData data = { None }; /* Check value type */ switch(rb_type(value)) { case T_SYMBOL: /* {{{ */ { /* Find symbol and add flag */ if(CHAR2SYM("ViewNext") == value) { type = SUB_GRAB_VIEW_SELECT; data = DATA((unsigned long)SUB_VIEW_NEXT); } else if(CHAR2SYM("ViewPrev") == value) { type = SUB_GRAB_VIEW_SELECT; data = DATA((unsigned long)SUB_VIEW_PREV); } else if(CHAR2SYM("SubtleReload") == value) { type = SUB_GRAB_SUBTLE_RELOAD; } else if(CHAR2SYM("SubtleRestart") == value) { type = SUB_GRAB_SUBTLE_RESTART; } else if(CHAR2SYM("SubtleQuit") == value) { type = SUB_GRAB_SUBTLE_QUIT; } else if(CHAR2SYM("WindowMove") == value) { type = SUB_GRAB_WINDOW_MOVE; } else if(CHAR2SYM("WindowMoveUp") == value) { type = SUB_GRAB_WINDOW_MOVE; data = DATA((unsigned long)SUB_GRAB_DIRECTION_UP); } else if(CHAR2SYM("WindowMoveRight") == value) { type = SUB_GRAB_WINDOW_MOVE; data = DATA((unsigned long)SUB_GRAB_DIRECTION_RIGHT); } else if(CHAR2SYM("WindowMoveDown") == value) { type = SUB_GRAB_WINDOW_MOVE; data = DATA((unsigned long)SUB_GRAB_DIRECTION_DOWN); } else if(CHAR2SYM("WindowMoveLeft") == value) { type = SUB_GRAB_WINDOW_MOVE; data = DATA((unsigned long)SUB_GRAB_DIRECTION_LEFT); } else if(CHAR2SYM("WindowResize") == value) { type = SUB_GRAB_WINDOW_RESIZE; } else if(CHAR2SYM("WindowResizeUp") == value) { type = SUB_GRAB_WINDOW_RESIZE; data = DATA((unsigned long)SUB_GRAB_DIRECTION_UP); } else if(CHAR2SYM("WindowResizeRight") == value) { type = SUB_GRAB_WINDOW_RESIZE; data = DATA((unsigned long)SUB_GRAB_DIRECTION_RIGHT); } else if(CHAR2SYM("WindowResizeDown") == value) { type = SUB_GRAB_WINDOW_RESIZE; data = DATA((unsigned long)SUB_GRAB_DIRECTION_DOWN); } else if(CHAR2SYM("WindowResizeLeft") == value) { type = SUB_GRAB_WINDOW_RESIZE; data = DATA((unsigned long)SUB_GRAB_DIRECTION_LEFT); } else if(CHAR2SYM("WindowFloat") == value) { type = SUB_GRAB_WINDOW_TOGGLE; data = DATA((unsigned long)SUB_CLIENT_MODE_FLOAT); } else if(CHAR2SYM("WindowFull") == value) { type = SUB_GRAB_WINDOW_TOGGLE; data = DATA((unsigned long)SUB_CLIENT_MODE_FULL); } else if(CHAR2SYM("WindowStick") == value) { type = SUB_GRAB_WINDOW_TOGGLE; data = DATA((unsigned long)SUB_CLIENT_MODE_STICK); } else if(CHAR2SYM("WindowZaphod") == value) { type = SUB_GRAB_WINDOW_TOGGLE; data = DATA((unsigned long)SUB_CLIENT_MODE_ZAPHOD); } else if(CHAR2SYM("WindowRaise") == value) { type = SUB_GRAB_WINDOW_STACK; data = DATA((unsigned long)SUB_CLIENT_RESTACK_UP); } else if(CHAR2SYM("WindowLower") == value) { type = SUB_GRAB_WINDOW_STACK; data = DATA((unsigned long)SUB_CLIENT_RESTACK_DOWN); } else if(CHAR2SYM("WindowLeft") == value) { type = SUB_GRAB_WINDOW_SELECT; data = DATA((unsigned long)SUB_GRAB_DIRECTION_LEFT); } else if(CHAR2SYM("WindowDown") == value) { type = SUB_GRAB_WINDOW_SELECT; data = DATA((unsigned long)SUB_GRAB_DIRECTION_DOWN); } else if(CHAR2SYM("WindowUp") == value) { type = SUB_GRAB_WINDOW_SELECT; data = DATA((unsigned long)SUB_GRAB_DIRECTION_UP); } else if(CHAR2SYM("WindowRight") == value) { type = SUB_GRAB_WINDOW_SELECT; data = DATA((unsigned long)SUB_GRAB_DIRECTION_RIGHT); } else if(CHAR2SYM("WindowKill") == value) { type = SUB_GRAB_WINDOW_KILL; } /* Symbols with parameters */ if(-1 == type) { const char *name = SYM2CHAR(value); /* Numbered grabs */ if(!strncmp(name, "ViewJump", 8)) { if((name = (char *)name + 8)) { type = SUB_GRAB_VIEW_FOCUS; data = DATA((unsigned long)(atol(name) - 1)); } } else if(!strncmp(name, "ViewSwitch", 10)) { if((name = (char *)name + 10)) { type = SUB_GRAB_VIEW_SWAP; data = DATA((unsigned long)(atol(name) - 1)); } } else if(!strncmp(name, "ScreenJump", 10)) { if((name = (char *)name + 10)) { type = SUB_GRAB_SCREEN_JUMP; data = DATA((unsigned long)(atol(name) - 1)); } } else { /* Sublet grabs? */ type = SUB_RUBY_DATA; data = DATA((unsigned long)value); } } } break; /* }}} */ case T_ARRAY: /* {{{ */ type = (SUB_GRAB_WINDOW_GRAVITY|SUB_RUBY_DATA); data = DATA((unsigned long)value); rb_ary_push(shelter, value); ///< Protect from GC break; /* }}} */ case T_STRING: /* {{{ */ type = SUB_GRAB_SPAWN; data = DATA(strdup(RSTRING_PTR(value))); break; /* }}} */ case T_DATA: /* {{{ */ type = SUB_GRAB_PROC; data = DATA(value); rb_ary_push(shelter, value); ///< Protect from GC break; /* }}} */ default: subSubtleLogWarn("Unexpected value type for grab `%s'\n", rb_obj_classname(value)); return; } /* Check value type */ if(T_STRING == rb_type(keys)) { /* Skip on checking only */ if(!(subtle->flags & SUB_SUBTLE_CHECK)) { int duplicate = False, ntok = 0; char *tokens = NULL, *tok = NULL, *save = NULL; SubGrab *prev = NULL, *g = NULL; /* Parse keys */ tokens = strdup(RSTRING_PTR(keys)); tok = strtok_r(tokens, " ", &save); while(tok) { /* Find or create grab */ if((g = subGrabNew(tok, &duplicate))) { if(!duplicate) { subArrayPush(subtle->grabs, (void *)g); subArraySort(subtle->grabs, subGrabCompare); } /* Assemble chain */ if(0 < ntok) { /* Init chain array */ if(NULL == prev->keys) { /* Mark first grab as chain start */ if(1 == ntok) prev->flags |= SUB_GRAB_CHAIN_START; prev->keys = subArrayNew(); } /* Mark grabs w/o action as chain link */ if(0 == (g->flags & ~(SUB_TYPE_GRAB| SUB_GRAB_KEY|SUB_GRAB_MOUSE|SUB_GRAB_CHAIN_LINK))) g->flags |= SUB_GRAB_CHAIN_LINK; subArrayPush(prev->keys, (void *)g); } prev = g; } tok = strtok_r(NULL, " ", &save); ntok++; } /* Add type/action to new grabs and mark as chain end */ if(g && !duplicate) { /* Update flags */ if(g->flags & SUB_GRAB_CHAIN_LINK) g->flags &= ~SUB_GRAB_CHAIN_LINK; if(1 < ntok) g->flags |= SUB_GRAB_CHAIN_END; g->flags |= type; g->data = data; } else if(type & SUB_GRAB_SPAWN) free(data.string); free(tokens); } } else rb_raise(rb_eArgError, "Unknown value type for grab"); } /* }}} */ /* RubyEvalPanel {{{ */ void RubyEvalPanel(VALUE ary, int position, SubScreen *s) { /* Check value type */ if(T_ARRAY == rb_type(ary)) { int i, j, flags = 0; Window panel = s->panel1; VALUE entry = Qnil, tray = Qnil, spacer = Qnil, separator = Qnil; VALUE sublets = Qnil, views = Qnil, title = Qnil, keychain; VALUE center = Qnil, subtlext = Qnil; SubPanel *p = NULL, *last = NULL;; /* Get syms */ tray = CHAR2SYM("tray"); spacer = CHAR2SYM("spacer"); center = CHAR2SYM("center"); separator = CHAR2SYM("separator"); sublets = CHAR2SYM("sublets"); views = CHAR2SYM("views"); title = CHAR2SYM("title"); keychain = CHAR2SYM("keychain"); /* Set position of panel */ if(SUB_SCREEN_PANEL2 == position) panel = s->panel2; /* Parse array and assemble panel */ for(i = 0; Qnil != (entry = rb_ary_entry(ary, i)); ++i) { p = NULL; /* Parse array and assemble panel */ if(entry == spacer || entry == center || entry == separator) { /* Add spacer after panel item */ if(last && flags & SUB_PANEL_SPACER1) { last->flags |= SUB_PANEL_SPACER2; flags &= ~SUB_PANEL_SPACER1; } /* Add separator after panel item */ if(last && flags & SUB_PANEL_SEPARATOR1) { last->flags |= SUB_PANEL_SEPARATOR2; flags &= ~SUB_PANEL_SEPARATOR1; } /* Add flags */ if(entry == spacer) flags |= SUB_PANEL_SPACER1; else if(entry == center) flags |= SUB_PANEL_CENTER; else if(entry == separator) flags |= SUB_PANEL_SEPARATOR1; } else if(entry == sublets) { if(0 < subtle->sublets->ndata) { /* Add dummy panel as entry point for sublets */ flags |= SUB_PANEL_SUBLETS; p = PANEL(subSharedMemoryAlloc(1, sizeof(SubPanel))); } } else if(entry == tray) { /* Prevent multiple use */ if(!subtle->panels.tray.screen) { subtle->flags |= SUB_SUBTLE_TRAY; flags |= (SUB_TYPE_PANEL|SUB_PANEL_TRAY); p = &subtle->panels.tray; XReparentWindow(subtle->dpy, subtle->windows.tray, panel, 0, 0); } } else if(entry == views) { /* Create new panel views */ p = subPanelNew(SUB_PANEL_VIEWS); } else if(entry == title) { /* Create new panel title */ p = subPanelNew(SUB_PANEL_TITLE); } else if(entry == keychain) { /* Prevent multiple use */ if(!subtle->panels.keychain.screen) { flags |= (SUB_TYPE_PANEL|SUB_PANEL_KEYCHAIN); p = &subtle->panels.keychain; p->keychain = KEYCHAIN(subSharedMemoryAlloc(1, sizeof(SubKeychain))); } } else if(T_DATA == rb_type(entry)) { if(NIL_P(subtlext)) subtlext = rb_const_get(rb_mKernel, rb_intern("Subtlext")); if(rb_obj_is_instance_of(entry, rb_const_get(subtlext, rb_intern("Icon")))) { /* Create new panel icon */ p = subPanelNew(SUB_PANEL_ICON); RubyIconToIcon(entry, p->icon); rb_ary_push(shelter, entry); ///< Protect from GC } } else { /* Check for sublets */ for(j = 0; j < subtle->sublets->ndata; j++) { SubPanel *p2 = PANEL(subtle->sublets->data[j]); if(entry == CHAR2SYM(p2->sublet->name)) { /* Check if sublet is already on a panel */ if(p2->screen) { /* Clone sublet */ p = subPanelNew(SUB_PANEL_COPY); p->flags |= (p2->flags & (SUB_PANEL_SUBLET| SUB_PANEL_DOWN|SUB_PANEL_OVER|SUB_PANEL_OUT)); p->sublet = p2->sublet; printf("Cloned sublet (%s)\n", p->sublet->name); } else p = p2; break; } } } /* Finally add to panel */ if(p) { p->flags |= SUB_SCREEN_PANEL2 == position ? (flags|SUB_PANEL_BOTTOM) : flags; ///< Mark for bottom panel p->screen = s; s->flags |= position; ///< Enable this panel flags = 0; last = p; subArrayPush(s->panels, (void *)p); } } /* Add stuff to last item */ if(last && flags & SUB_PANEL_SPACER1) last->flags |= SUB_PANEL_SPACER2; if(last && flags & SUB_PANEL_SEPARATOR1) last->flags |= SUB_PANEL_SEPARATOR2; subRubyRelease(ary); } } /* }}} */ /* RubyEvalConfig {{{ */ static void RubyEvalConfig(void) { int i; /* Update style values */ subStyleUpdate(); /* Check and update grabs */ if(0 == subtle->grabs->ndata) { subSubtleLogWarn("Cannot find any grabs\n"); subSubtleFinish(); exit(-1); } else { subArraySort(subtle->grabs, subGrabCompare); /* Update grabs and gravites */ for(i = 0; i < subtle->grabs->ndata; i++) { SubGrab *g = GRAB(subtle->grabs->data[i]); /* Update gravity grab */ if(g->flags & SUB_GRAB_WINDOW_GRAVITY) { char *string = NULL; /* Create gravity string */ RubyValueToGravityString(g->data.num, &string); subRubyRelease(g->data.num); g->data.string = string; g->flags &= ~SUB_RUBY_DATA; } } } /* Check gravities */ if(0 == subtle->gravities->ndata) { subSubtleLogError("Cannot find any gravities\n"); subSubtleFinish(); exit(-1); } /* Get default gravity or use default (-1) */ subtle->gravity = RubyValueToGravity(subtle->gravity); subGravityPublish(); /* Check and update screens */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); /* Check if vid exists */ if(0 > s->viewid || s->viewid >= subtle->views->ndata) s->viewid = 0; s->flags &= ~SUB_RUBY_DATA; } /* Check and update tags */ for(i = 0; i < subtle->tags->ndata; i++) { SubTag *t = TAG(subtle->tags->data[i]); /* Update gravities */ if(t->flags & SUB_TAG_GRAVITY) { /* Disable tag gravity when gravity can't be found */ if(-1 == (t->gravityid = RubyValueToGravity(t->gravityid))) t->flags &= ~SUB_TAG_GRAVITY; } /* Update screens */ if(t->flags & SUB_CLIENT_MODE_STICK) { if(-1 != t->screenid && (0 > t->screenid || subtle->screens->ndata <= t->screenid)) t->flags &= ~SUB_CLIENT_MODE_STICK; } } subTagPublish(); /* Check tag count */ if(1 == subtle->tags->ndata) { subSubtleLogWarn("Cannot find any tags\n"); } else if(32 < subtle->tags->ndata) subSubtleLogWarn("Cannot handle more than 32 tags\n"); /* Check and update views */ if(0 == subtle->views->ndata) ///< Create default view { SubView *v = subViewNew("subtle", "default"); subArrayPush(subtle->views, (void *)v); subSubtleLogError("Cannot find any views\n"); } else ///< Check default tag { SubView *v = NULL; /* Check for view with default tag */ for(i = subtle->views->ndata - 1; 0 <= i; i--) if((v = VIEW(subtle->views->data[i])) && v->tags & DEFAULTTAG) { subSubtleLogDebugRuby("EvalConfig: default view=%s\n", v->name); break; } v->tags |= DEFAULTTAG; ///< Set default tag } subViewPublish(); subDisplayPublish(); } /* }}} */ /* RubyEvalStyle {{{ */ static void RubyEvalStyle(VALUE name, SubStyle *s, VALUE params) { int bw = -1; long border = -1; VALUE value = Qnil; /* Special cases */ if(CHAR2SYM("subtle") == name) { /* Get colors */ RubyHashToColor(params, "background", &s->bg); RubyHashToColor(params, "panel_top", &s->top); RubyHashToColor(params, "panel_bottom", &s->bottom); RubyHashToColor(params, "stipple", &s->fg); /* Set strut */ RubyHashToSides(params, "strut", &s->padding); RubyHashToSides(params, "padding", &s->padding); /* Set both panel colors */ if(!NIL_P(value = rb_hash_lookup(params, CHAR2SYM("panel")))) { RubyHashToColor(params, "panel", &s->top); s->bottom = s->top; } } else if(CHAR2SYM("clients") == name) { /* We misuse some style values here: * border-top <-> client border * border-right <-> title length * margin <-> client gap */ /* Set border color and width */ RubyHashToBorder(params, "active", &s->fg, &s->border.top); RubyHashToBorder(params, "inactive", &s->bg, &s->border.top); /* Get client margin */ RubyHashToSides(params, "margin", &s->margin); /* FIXME: Set title width, but that should be a title style, right? */ if(FIXNUM_P(value = rb_hash_lookup(params, CHAR2SYM("width")))) s->right = FIX2INT(value); else s->right = 50; } else { /* Get colors */ RubyHashToColor(params, "foreground", &s->fg); RubyHashToColor(params, "background", &s->bg); RubyHashToColor(params, "icon", &s->icon); /* Set all borders */ RubyHashToBorder(params, "border", &border, &bw); /* Get borders */ RubyHashToBorder(params, "border_top", &s->top, &s->border.top); RubyHashToBorder(params, "border_right", &s->right, &s->border.right); RubyHashToBorder(params, "border_bottom", &s->bottom, &s->border.bottom); RubyHashToBorder(params, "border_left", &s->left, &s->border.left); /* Apply catchall values */ if(-1 != border) s->top = s->right = s->bottom = s->left = border; if(-1 != bw) { s->border.top = s->border.right = s->border.bottom = s->border.left = bw; } /* Get padding/margin */ RubyHashToSides(params, "padding", &s->padding); RubyHashToSides(params, "margin", &s->margin); /* Get min width */ RubyHashToInt(params, "min_width", &s->min); s->min = MAX(0, s->min); /* Style font */ if(T_STRING == rb_type(value = rb_hash_lookup(params, CHAR2SYM("font"))) && !s->font) { s->flags |= SUB_STYLE_FONT; s->font = RubyFont(RSTRING_PTR(value)); /* EWMH: Font */ if(CHAR2SYM("all") == name) subEwmhSetString(ROOT, SUB_EWMH_SUBTLE_FONT, RSTRING_PTR(value)); } /* Style separator */ if(T_STRING == rb_type(value = rb_hash_lookup(params, CHAR2SYM("separator"))) && !s->separator) { s->flags |= SUB_STYLE_SEPARATOR; /* Create new separator */ s->separator = (SubSeparator *)subSharedMemoryAlloc(1, sizeof(SubSeparator)); s->separator->string = strdup(RSTRING_PTR(value)); } } } /* }}} */ /* Foreach */ /* RubyForeachMatcher {{{ */ static int RubyForeachMatcher(VALUE key, VALUE value, VALUE data) { int type = 0; VALUE regex = Qnil, *rargs = (VALUE *)data; if(key == Qundef) return ST_CONTINUE; /* Check key value type */ switch(rb_type(key)) { case T_NIL: type = SUB_TAG_MATCH_INSTANCE|SUB_TAG_MATCH_CLASS; ///< Defaults break; case T_SYMBOL: RubySymbolToFlag(key, &type); break; case T_ARRAY: { int i; VALUE entry = Qnil; /* Check flags in array */ for(i = 0; Qnil != (entry = rb_ary_entry(key, i)); i++) RubySymbolToFlag(entry, &type); } break; default: rb_raise(rb_eArgError, "Unknown value type"); } /* Check value type */ switch(rb_type(value)) { case T_REGEXP: regex = rb_funcall(value, rb_intern("source"), 0, NULL); break; case T_SYMBOL: RubySymbolToFlag(value, &type); break; case T_ARRAY: { int i; VALUE entry = Qnil; /* Check flags in array */ for(i = 0; Qnil != (entry = rb_ary_entry(value, i)); i++) RubySymbolToFlag(entry, &type); } break; case T_STRING: regex = value; break; default: rb_raise(rb_eArgError, "Unknown value type"); } /* Finally create regex if there is any and append additional flags */ if(0 < type) { subTagMatcherAdd(TAG(rargs[0]), type, NIL_P(regex) ? NULL : RSTRING_PTR(regex), 0 < rargs[1]++); } return ST_CONTINUE; } /* }}} */ /* RubyForeachStyle {{{ */ static int RubyForeachStyle(VALUE key, VALUE value, VALUE data) { SubStyle *s = (SubStyle *)data, *style = NULL; if(key == Qundef || NIL_P(value)) return ST_CONTINUE; /* Create new state */ style = subStyleNew(); style->name = strdup(SYM2CHAR(key)); RubyEvalStyle(key, style, rb_iv_get(value, "@params")); /* Ease access to sub-styles */ if(&subtle->styles.views == s) { if(CHAR2SYM("urgent") == key) subtle->styles.urgent = style; else if(CHAR2SYM("occupied") == key) subtle->styles.occupied = style; else if(CHAR2SYM("focus") == key) subtle->styles.focus = style; else if(CHAR2SYM("visible") == key) subtle->styles.visible = style; else if(CHAR2SYM("separator") == key) subtle->styles.viewsep = style; else if(CHAR2SYM("unoccupied") == key) { subSubtleLogDeprecated("The `unoccupied' style is deprecated, " "please use the `views' style for normal views instead.\n"); subStyleMerge(&subtle->styles.views, style); subStyleKill(style); return ST_CONTINUE; } } else if(&subtle->styles.sublets == s) if(CHAR2SYM("separator") == key) subtle->styles.subletsep = style; /* Finally add style */ if(!s->styles) s->styles = subArrayNew(); subArrayPush(s->styles, (void *)style); return ST_CONTINUE; } /* }}} */ /* Wrap */ /* RubyWrapLoadSubtlext {{{ */ static VALUE RubyWrapLoadSubtlext(VALUE data) { return rb_require("subtle/subtlext"); }/* }}} */ /* RubyWrapLoadPanels {{{ */ static VALUE RubyWrapLoadPanels(VALUE data) { int i; /* Pass 1: Load actual panels */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); if(!s->panels) s->panels = subArrayNew(); RubyEvalPanel(s->top, SUB_SCREEN_PANEL1, s); RubyEvalPanel(s->bottom, SUB_SCREEN_PANEL2, s); } /* Pass 2: Add remaining sublets if any */ if(0 < subtle->sublets->ndata) { int j, k, pos; for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); Window panel = s->panel1; for(j = 0; j < s->panels->ndata; j++) { SubPanel *p = PANEL(s->panels->data[j]); if(panel != s->panel2 && p->flags & SUB_PANEL_BOTTOM) panel = s->panel2; /* Find dummy panel */ if(p->flags & SUB_PANEL_SUBLETS) { SubPanel *sublet = NULL; pos = j; subArrayRemove(s->panels, (void *)p); /* Find sublets not on any panel so far */ for(k = 0; k < subtle->sublets->ndata; k++) { sublet = PANEL(subtle->sublets->data[k]); /* Check if screen is empty */ if(NULL == sublet->screen) { sublet->flags |= SUB_PANEL_SEPARATOR2; sublet->screen = s; /* Mark for bottom panel */ if(p->flags & SUB_PANEL_BOTTOM) sublet->flags |= SUB_PANEL_BOTTOM; subArrayInsert(s->panels, pos++, (void *)sublet); } } /* Check spacers and separators in first and last sublet */ if((sublet = PANEL(subArrayGet(s->panels, j)))) sublet->flags |= (p->flags & (SUB_PANEL_BOTTOM| SUB_PANEL_SPACER1|SUB_PANEL_SEPARATOR1|SUB_PANEL_CENTER)); if((sublet = PANEL(subArrayGet(s->panels, pos - 1)))) { sublet->flags |= (p->flags & SUB_PANEL_SPACER2); if(!(p->flags & SUB_PANEL_SEPARATOR2)) sublet->flags &= ~SUB_PANEL_SEPARATOR2; } free(p); break; } } } /* Unloaded non-visible sublets */ for(i = 0; i < subtle->sublets->ndata; i++) { SubPanel *p = PANEL(subtle->sublets->data[i]); if(p->flags & SUB_PANEL_SUBLET && !p->screen) subRubyUnloadSublet(p); } /* Finally sort sublets */ subArraySort(subtle->sublets, subPanelCompare); } return Qnil; } /* }}} */ /* RubyWrapCall {{{ */ static VALUE RubyWrapCall(VALUE data) { VALUE *rargs = (VALUE *)data; /* Check call type */ switch((int)rargs[0]) { case SUB_CALL_CONFIGURE: /* {{{ */ rb_funcall(rargs[1], rb_intern("__configure"), 1, rargs[1]); break; /* }}} */ case SUB_CALL_RUN: /* {{{ */ rb_funcall(rargs[1], rb_intern("__run"), 1, rargs[1]); break; /* }}} */ case SUB_CALL_DATA: /* {{{ */ { int nlist = 0; char **list = NULL; Atom prop = subEwmhGet(SUB_EWMH_SUBTLE_DATA); VALUE meth = rb_intern("__data"), str = Qnil; /* Fetch data or create empty string */ if((list = subSharedPropertyGetStrings(subtle->dpy, ROOT, prop, &nlist))) { if(list && 0 < nlist) str = rb_str_new2(list[0]); XFreeStringList(list); } else str = rb_str_new2(""); subSharedPropertyDelete(subtle->dpy, ROOT, prop); /* Finally call method */ rb_funcall(rargs[1], meth, MINMAX(rb_obj_method_arity(rargs[1], meth), 1, 2), rargs[1], str); } break; /* }}} */ case SUB_CALL_WATCH: /* {{{ */ rb_funcall(rargs[1], rb_intern("__watch"), 1, rargs[1]); break; /* }}} */ case SUB_CALL_DOWN: /* {{{ */ { int *args = (int *)rargs[2]; VALUE meth = rb_intern("__down"); rb_funcall(rargs[1], meth, MINMAX(rb_obj_method_arity(rargs[1], meth), 1, 4), rargs[1], INT2FIX(args[0]), INT2FIX(args[1]), INT2FIX(args[2])); } break; /* }}} */ case SUB_CALL_OVER: /* {{{ */ rb_funcall(rargs[1], rb_intern("__over"), 1, rargs[1]); break; /* }}} */ case SUB_CALL_OUT: /* {{{ */ rb_funcall(rargs[1], rb_intern("__out"), 1, rargs[1]); break; /* }}} */ case SUB_CALL_UNLOAD: /* {{{ */ rb_funcall(rargs[1], rb_intern("__unload"), 1, rargs[1]); break; /* }}} */ default: /* {{{ */ /* Call instance methods or just a proc */ if(rb_obj_is_instance_of(rargs[1], rb_cMethod)) { int arity = 0; VALUE receiver = Qnil; /* Get arity */ receiver = rb_funcall(rargs[1], rb_intern("receiver"), 0, NULL); arity = FIX2INT(rb_funcall(rargs[1], rb_intern("arity"), 0, NULL)); arity = -1 == arity ? 2 : MINMAX(arity, 1, 2); rb_funcall(rargs[1], rb_intern("call"), arity, receiver, RubySubtleToSubtlext((VALUE *)rargs[2])); subScreenUpdate(); subScreenRender(); } else { rb_funcall(rargs[1], rb_intern("call"), MINMAX(rb_proc_arity(rargs[1]), 0, 1), RubySubtleToSubtlext((VALUE *)rargs[2])); } break; /* }}} */ } return Qnil; } /* }}} */ /* RubyWrapRelease {{{ */ static VALUE RubyWrapRelease(VALUE value) { /* Relase value from shelter */ if(Qtrue == rb_ary_includes(shelter, value)) rb_ary_delete(shelter, value); return Qnil; } /* }}} */ /* RubyWrapEvalFile {{{ */ static VALUE RubyWrapEvalFile(VALUE data) { VALUE *rargs = (VALUE *)data, rargs2[3] = { Qnil }; /* Wrap data */ rargs2[0] = rb_funcall(rb_cFile, rb_intern("read"), 1, rargs[0]); rargs2[1] = rargs[0]; rargs2[2] = rargs[1]; rb_obj_instance_eval(2, rargs2, rargs[1]); return Qnil; } /* }}} */ /* RubyWrapSubletConfig {{{ */ static VALUE RubyWrapSubletConfig(VALUE data) { VALUE *rargs = (VALUE *)data, hash = Qnil; SubSublet *s = SUBLET(rargs[1]); /* Check if config hash exists */ if(T_HASH == rb_type(hash = rb_hash_lookup(config_sublets, rargs[0]))) { VALUE value = Qnil; /* Set sublet interval */ if(FIXNUM_P(value = rb_hash_lookup(hash, CHAR2SYM("interval")))) s->interval = FIX2INT(value); /* Set sublet style */ if(T_SYMBOL == rb_type(value = rb_hash_lookup(hash, CHAR2SYM("style")))) { value = rb_sym_to_s(value); subStyleFind(&subtle->styles.sublets, RSTRING_PTR(value), &s->styleid); } } /* Check if there is a matching style */ subStyleFind(&subtle->styles.sublets, s->name, &s->styleid); return Qnil; } /* }}} */ /* Object */ /* RubyObjectDispatcher {{{ */ /* * Dispatcher for Subtlext constants - internal use only */ static VALUE RubyObjectDispatcher(VALUE self, VALUE missing) { VALUE ret = Qnil; /* Load subtlext on demand */ if(CHAR2SYM("Subtlext") == missing) { int state = 0; ID id = SYM2ID(missing); /* Carefully load sublext */ rb_protect(RubyWrapLoadSubtlext, Qnil, &state); if(state) { subSubtleLogWarn("Cannot load subtlext\n"); RubyBacktrace(); subSubtleFinish(); exit(-1); } ret = rb_const_get(rb_mKernel, id); } return ret; } /* }}} */ /* Options */ /* RubyOptionsInit {{{ */ /* * call-seq: init -> Subtle::Options * * Create a new Options object * * options = Subtle::Options.new * => # */ static VALUE RubyOptionsInit(VALUE self) { rb_iv_set(self, "@params", rb_hash_new()); return Qnil; } /* }}} */ /* RubyOptionsDispatcher {{{ */ /* * Dispatcher for DSL proc methods - internal use only */ static VALUE RubyOptionsDispatcher(int argc, VALUE *argv, VALUE self) { VALUE ret = Qnil, missing = Qnil, args[4] = { Qnil }; rb_scan_args(argc, argv, "23", &missing, &args[0], &args[1], &args[2], &args[3]); /* Check whether missing is included in methods array * and dispatch to config or just store param*/ if(RTEST(config_methods) && rb_ary_includes(config_methods, missing)) { ret = rb_funcall2(config_instance, rb_to_id(missing), --argc, ++argv); } else { VALUE arg = Qnil; /* Convert multiple argument to one array */ if(2 < argc) { int i; /* Move args into array */ arg = rb_ary_new(); for(i = 0; i < LENGTH(args); i++) if(!NIL_P(args[i])) rb_ary_push(arg, args[i]); } else arg = args[0]; ret = rb_hash_aset(rb_iv_get(self, "@params"), missing, arg); } return ret; } /* }}} */ /* RubyOptionsMatch {{{ */ /* * call-seq: match -> nil * * Append match hashes if called multiple times * * option.match :name => /foo/ * => nil */ static VALUE RubyOptionsMatch(VALUE self, VALUE value) { VALUE params = Qnil, ary = Qnil, hash = Qnil, sym = Qnil; /* Get params and convert value to hash */ params = rb_iv_get(self, "@params"); hash = RubyValueToHash(value); sym = CHAR2SYM("match"); /* Check if hash contains key and add or otherwise create it */ if(T_ARRAY != rb_type(ary = rb_hash_lookup(params, sym))) { ary = rb_ary_new(); rb_hash_aset(params, sym, ary); } /* Finally add value to array */ rb_ary_push(ary, hash); return Qnil; } /* }}} */ /* RubyOptionsGravity {{{ */ /* * call-seq: gravity -> nil * * Overwrite global gravity method * * option.gravity :center * => nil */ static VALUE RubyOptionsGravity(VALUE self, VALUE gravity) { VALUE params = rb_iv_get(self, "@params"); /* Just store param */ return rb_hash_aset(params, CHAR2SYM("gravity"), gravity); } /* }}} */ /* RubyOptionsStyle {{{ */ /* * call-seq: style(name) -> nil * * Overwrite global style method * * option.style :urgent do * foreground "#fecf35" * end * => nil */ static VALUE RubyOptionsStyle(VALUE self, VALUE name) { /* Check if block is given */ if(rb_block_given_p()) { VALUE klass = Qnil, options = Qnil, styles = rb_iv_get(self, "@styles"); /* Create styles hash if necessary */ if(NIL_P(styles)) { styles = rb_hash_new(); rb_iv_set(self, "@styles", styles); } /* Collect options */ klass = rb_const_get(mod, rb_intern("Options")); options = rb_funcall(klass, rb_intern("new"), 1, self); rb_obj_instance_eval(0, 0, options); rb_hash_aset(styles, name, options); } else { VALUE params = rb_iv_get(self, "@params"); /* Just append to params */ rb_hash_aset(params, CHAR2SYM("style"), name); } return Qnil; } /* }}} */ /* RubyOptionsOnMatch {{{ */ /* * call-seq: on_match(, blk) -> nil * * Add a tag on match proc * * tag "test" do * match "foobar" * * on_match do |c| * c.gravity = :foobar * end * end */ static VALUE RubyOptionsOnMatch(int argc, VALUE *argv, VALUE self) { VALUE value = Qnil; rb_scan_args(argc, argv, "01", &value); if(subtle->flags & SUB_SUBTLE_CHECK) return Qnil; ///< Skip on check if(rb_block_given_p()) value = rb_block_proc(); ///< Get proc rb_hash_aset(rb_iv_get(self, "@params"), CHAR2SYM("on_match"), value); return Qnil; } /* }}} */ /* Config */ /* RubyConfigMissing {{{ */ /* * Check error of missing methods */ static VALUE RubyConfigMissing(int argc, VALUE *argv, VALUE self) { char *name = NULL; VALUE missing = Qnil, args = Qnil; rb_scan_args(argc, argv, "1*", &missing, &args); name = (char *)rb_id2name(SYM2ID(missing)); subSubtleLogWarn("Cannot find method `%s'\n", name); return Qnil; } /* }}} */ /* RubyConfigAdded {{{ */ /* * Updated methods list when singleton methods are added */ static VALUE RubyConfigAdded(VALUE self, VALUE name) { /* Append method name to methods list */ rb_ary_push(config_methods, name); subSubtleLogDebugRuby("Added: singleton method=`%s'\n", SYM2CHAR(name)); return Qnil; } /* }}} */ /* RubyConfigSet {{{ */ /* * call-seq: set(option, value) -> nil * * Set options * * set :urgent, true */ static VALUE RubyConfigSet(VALUE self, VALUE option, VALUE value) { /* Check value type */ if(T_SYMBOL == rb_type(option)) { switch(rb_type(value)) { case T_FIXNUM: /* {{{ */ if(CHAR2SYM("step") == option || CHAR2SYM("increase_step") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK)) subtle->step = FIX2INT(value); } else if(CHAR2SYM("snap") == option || CHAR2SYM("border_snap") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK)) subtle->snap = FIX2INT(value); } else if(CHAR2SYM("gravity") == option || CHAR2SYM("default_gravity") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK)) subtle->gravity = value; ///< Store for later } else subSubtleLogWarn("Unknown option `:%s'\n", SYM2CHAR(option)); break; /* }}} */ case T_SYMBOL: /* {{{ */ if(CHAR2SYM("gravity") == option || CHAR2SYM("default_gravity") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK)) subtle->gravity = value; ///< Store for later } else subSubtleLogWarn("Unknown option `:%s'\n", SYM2CHAR(option)); break; /* }}} */ case T_TRUE: case T_FALSE: /* {{{ */ if(CHAR2SYM("urgent") == option || CHAR2SYM("urgent_dialogs") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK) && Qtrue == value) subtle->flags |= SUB_SUBTLE_URGENT; } else if(CHAR2SYM("resize") == option || CHAR2SYM("honor_size_hints") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK) && Qtrue == value) subtle->flags |= SUB_SUBTLE_RESIZE; } else if(CHAR2SYM("tiling") == option || CHAR2SYM("gravity_tiling") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK) && Qtrue == value) subtle->flags |= SUB_SUBTLE_TILING; } else if(CHAR2SYM("click_to_focus") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK) && Qtrue == value) subtle->flags |= SUB_SUBTLE_FOCUS_CLICK; } else if(CHAR2SYM("skip_pointer_warp") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK) && Qtrue == value) subtle->flags |= SUB_SUBTLE_SKIP_WARP; } else if(CHAR2SYM("skip_urgent_warp") == option) { if(!(subtle->flags & SUB_SUBTLE_CHECK) && Qtrue == value) subtle->flags |= SUB_SUBTLE_SKIP_URGENT_WARP; } else subSubtleLogWarn("Unknown option `:%s'\n", SYM2CHAR(option)); break; /* }}} */ case T_STRING: /* {{{ */ if(CHAR2SYM("wmname") == option) { /* Set support window to root (broken Java) * and update WM_NAME */ if(!(subtle->flags & SUB_SUBTLE_CHECK)) { Window root = ROOT; subEwmhSetWindows(ROOT, SUB_EWMH_NET_SUPPORTING_WM_CHECK, &root, 1); subEwmhSetString(root, SUB_EWMH_NET_WM_NAME, RSTRING_PTR(value)); } } else subSubtleLogWarn("Unknown option `:%s'\n", SYM2CHAR(option)); break; /* }}} */ default: rb_raise(rb_eArgError, "Unexpected value type for option `%s'", SYM2CHAR(option)); } } else rb_raise(rb_eArgError, "Unexpected value type for set"); return Qnil; } /* }}} */ /* RubyConfigGravity {{{ */ /* * call-seq: gravity(name, value, tile) -> nil * * Create gravity and optionally enable tiling, either * horizonally (:horz) or vertically. (:vert) * * gravity :top_left, [0, 0, 50, 50] * * gravity :top_left, [0, 0, 50, 50], :vert */ static VALUE RubyConfigGravity(int argc, VALUE *argv, VALUE self) { VALUE name = Qnil, value = Qnil, tile = Qnil; rb_scan_args(argc, argv, "21", &name, &value, &tile); /* Check value type */ if(T_SYMBOL == rb_type(name) && T_ARRAY == rb_type(value)) { XRectangle geometry = { 0 }; RubyArrayToGeometry(value, &geometry); /* Skip on checking only */ if(!(subtle->flags & SUB_SUBTLE_CHECK)) { SubGravity *g = NULL; /* Finally create new gravity */ if((g = subGravityNew(SYM2CHAR(name), &geometry))) { /* Tile just clients with this gravity */ if(T_SYMBOL == rb_type(tile)) { if(CHAR2SYM("horz") == tile) g->flags |= SUB_GRAVITY_HORZ; else if(CHAR2SYM("vert") == tile) g->flags |= SUB_GRAVITY_VERT; } subArrayPush(subtle->gravities, (void *)g); } } } else rb_raise(rb_eArgError, "Unknown value type for gravity"); return Qnil; } /* }}} */ /* RubyConfigGrab {{{ */ /* * call-seq: grab(chain, value) -> nil * grab(chain, &blk) -> nil * * Create grabs * * grab "A-F1", :ViewJump1 */ static VALUE RubyConfigGrab(int argc, VALUE *argv, VALUE self) { VALUE chain = Qnil, value = Qnil; rb_scan_args(argc, argv, "11", &chain, &value); if(rb_block_given_p()) value = rb_block_proc(); ///< Get proc RubyEvalGrab(chain, value); return Qnil; } /* }}} */ /* RubyConfigTag {{{ */ /* * call-seq: tag(name, regex) -> nil * tag(name, blk) -> nil * * Add a new tag * * tag "foobar", "regex" * * tag "foobar" do * regex = "foobar" * end */ static VALUE RubyConfigTag(int argc, VALUE *argv, VALUE self) { int flags = 0, screenid = -1; unsigned long gravityid = 0; XRectangle geom = { 0 }; VALUE name = Qnil, match = Qnil, params = Qnil, value = Qnil, proc = Qnil; rb_scan_args(argc, argv, "11", &name, &match); /* Call proc */ if(rb_block_given_p()) { VALUE klass = Qnil, options = Qnil; /* Collect options */ klass = rb_const_get(mod, rb_intern("Options")); options = rb_funcall(klass, rb_intern("new"), 1, self); rb_obj_instance_eval(0, 0, options); params = rb_iv_get(options, "@params"); /* Check matcher */ if(T_ARRAY == rb_type(value = rb_hash_lookup(params, CHAR2SYM("match")))) match = value; ///< Lazy eval /* Set gravity */ if(T_SYMBOL == rb_type(value = rb_hash_lookup(params, CHAR2SYM("gravity"))) || T_FIXNUM == rb_type(value) || T_ARRAY == rb_type(value)) { flags |= SUB_TAG_GRAVITY; gravityid = value; ///< Lazy eval } /* Set geometry */ if(T_ARRAY == rb_type(value = rb_hash_lookup(params, CHAR2SYM("geometry")))) { flags |= SUB_TAG_GEOMETRY; RubyArrayToGeometry(value, &geom); } /* Set geometry */ if(T_ARRAY == rb_type(value = rb_hash_lookup(params, CHAR2SYM("position")))) { flags |= SUB_TAG_POSITION; RubyArrayToGeometry(value, &geom); } /* Set window type */ if(T_SYMBOL == rb_type(value = rb_hash_lookup(params, CHAR2SYM("type")))) { /* Check type */ if(CHAR2SYM("normal") == value) flags = SUB_CLIENT_TYPE_NORMAL; else if(CHAR2SYM("desktop") == value) flags = SUB_CLIENT_TYPE_DESKTOP; else if(CHAR2SYM("dock") == value) flags = SUB_CLIENT_TYPE_DOCK; else if(CHAR2SYM("toolbar") == value) flags = SUB_CLIENT_TYPE_TOOLBAR; else if(CHAR2SYM("splash") == value) flags = SUB_CLIENT_TYPE_SPLASH; else if(CHAR2SYM("dialog") == value) flags = SUB_CLIENT_TYPE_DIALOG; } /* Check state properties */ if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("borderless")))) flags |= SUB_CLIENT_MODE_BORDERLESS; if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("center")))) flags |= SUB_CLIENT_MODE_CENTER; if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("fixed")))) flags |= SUB_CLIENT_MODE_FIXED; if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("float")))) flags |= SUB_CLIENT_MODE_FLOAT; if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("full")))) flags |= SUB_CLIENT_MODE_FULL; if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("resize")))) flags |= SUB_CLIENT_MODE_RESIZE; if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("urgent")))) flags |= SUB_CLIENT_MODE_URGENT; if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("zaphod")))) flags |= SUB_CLIENT_MODE_ZAPHOD; /* Set stick screen */ if(RTEST(value = rb_hash_lookup(params, CHAR2SYM("stick")))) { /* Either screen id or just true */ if(FIXNUM_P(value)) { screenid = FIX2INT(value); flags |= SUB_CLIENT_MODE_STICK; } else if(Qtrue == value) flags |= SUB_CLIENT_MODE_STICK; } /* Set match proc */ if(RTEST(value = rb_hash_lookup(params, CHAR2SYM("on_match")))) { proc = value; flags |= SUB_TAG_PROC; rb_ary_push(shelter, proc); ///< Protect from GC } } /* Check value type */ if(T_STRING == rb_type(name)) { /* Skip on checking only */ if(!(subtle->flags & SUB_SUBTLE_CHECK)) { int duplicate = False; SubTag *t = NULL; /* Finally create and add new tag if no duplicate */ if((t = subTagNew(RSTRING_PTR(name), &duplicate)) && False == duplicate) { int i; VALUE entry = Qnil, rargs[2] = { 0 }; /* Set tag values */ t->flags |= flags; t->gravityid = gravityid; t->screenid = screenid; t->geom = geom; t->proc = proc; /* Add matcher */ rargs[0] = (VALUE)t; switch(rb_type(match)) { case T_ARRAY: for(i = 0; T_HASH == rb_type(entry = rb_ary_entry(match, i)); i++) { rargs[1] = 0; ///< Reset matcher count rb_hash_foreach(entry, RubyForeachMatcher, (VALUE)&rargs); } break; case T_REGEXP: case T_STRING: RubyForeachMatcher(Qnil, match, (VALUE)&rargs); } subArrayPush(subtle->tags, (void *)t); } } } else rb_raise(rb_eArgError, "Unknown value type for tag"); return Qnil; } /* }}} */ /* RubyConfigView {{{ */ /* * call-seq: view(name, regex) -> nil * * Add a new view * * view "foobar", "regex" */ static VALUE RubyConfigView(int argc, VALUE *argv, VALUE self) { int flags = 0; VALUE name = Qnil, match = Qnil, params = Qnil, value = Qnil, icon = value; rb_scan_args(argc, argv, "11", &name, &match); /* Call proc */ if(rb_block_given_p()) { VALUE klass = Qnil, options = Qnil; /* Collect options */ klass = rb_const_get(mod, rb_intern("Options")); options = rb_funcall(klass, rb_intern("new"), 1, self); rb_obj_instance_eval(0, 0, options); params = rb_iv_get(options, "@params"); /* Check match */ if(T_ARRAY == rb_type(value = rb_hash_lookup(params, CHAR2SYM("match")))) match = rb_hash_lookup(rb_ary_entry(value, 0), Qnil); ///< Lazy eval /* Check dynamic */ if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("dynamic")))) flags |= SUB_VIEW_DYNAMIC; /* Check icon only */ if(Qtrue == (value = rb_hash_lookup(params, CHAR2SYM("icon_only")))) flags |= SUB_VIEW_ICON_ONLY; /* Check icon */ icon = RubyValueToIcon(rb_hash_lookup(params, CHAR2SYM("icon"))); } /* Check value type */ if(T_STRING == rb_type(name)) { /* Skip on checking only */ if(!(subtle->flags & SUB_SUBTLE_CHECK)) { SubView *v = NULL; char *re = NULL; /* Convert type */ switch(rb_type(match)) { case T_REGEXP: match = rb_funcall(match, rb_intern("source"), 0, NULL); case T_STRING: re = RSTRING_PTR(match); break; } /* Finally create new view */ if((v = subViewNew(RSTRING_PTR(name), re))) { v->flags |= flags; subArrayPush(subtle->views, (void *)v); /* Add icon */ if(!NIL_P(icon)) { v->flags |= SUB_VIEW_ICON; v->icon = ICON(subSharedMemoryAlloc(1, sizeof(SubIcon))); RubyIconToIcon(icon, v->icon); rb_ary_push(shelter, icon); ///< Protect from GC } else v->flags &= ~SUB_VIEW_ICON_ONLY; } } } else rb_raise(rb_eArgError, "Unknown value type for view"); return Qnil; } /* }}} */ /* RubyConfigOn {{{ */ /* * call-seq: on(event, &block) -> nil * * Event block for hooks * * on :event do |s| * puts s.name * end */ static VALUE RubyConfigOn(int argc, VALUE *argv, VALUE self) { VALUE event = Qnil, value = Qnil; rb_scan_args(argc, argv, "11", &event, &value); /* Check value type */ if(T_SYMBOL == rb_type(event)) { if(subtle->flags & SUB_SUBTLE_CHECK) return Qnil; ///< Skip on check if(rb_block_given_p()) value = rb_block_proc(); ///< Get proc RubyEvalHook(event, value); } else rb_raise(rb_eArgError, "Unknown value type for on"); return Qnil; } /* }}} */ /* RubyConfigSublet {{{ */ /* * call-seq: sublet(name, blk) -> nil * * Configure a sublet * * sublet :jdownloader do * interval 20 * end */ static VALUE RubyConfigSublet(VALUE self, VALUE sublet) { VALUE klass = Qnil, options = Qnil; rb_need_block(); /* Check value type */ if(T_SYMBOL == rb_type(sublet)) { /* Collect options */ klass = rb_const_get(mod, rb_intern("Options")); options = rb_funcall(klass, rb_intern("new"), 1, self); rb_obj_instance_eval(0, 0, options); /* Clone to get rid of object instance and store it */ rb_hash_aset(config_sublets, sublet, rb_obj_clone(rb_iv_get(options, "@params"))); } else rb_raise(rb_eArgError, "Unknown value type for sublet"); return Qnil; } /* }}} */ /* RubyConfigScreen {{{ */ /* * call-seq: screen(name, blk) -> nil * * Set options for screen * * screen 1 do * stipple false * top [] * bottom [] * end */ static VALUE RubyConfigScreen(VALUE self, VALUE id) { VALUE params = Qnil, value = Qnil, klass = Qnil, options = Qnil; SubScreen *s = NULL; /* FIXME: rb_need_block() */ if(!rb_block_given_p()) return Qnil; /* Collect options */ klass = rb_const_get(mod, rb_intern("Options")); options = rb_funcall(klass, rb_intern("new"), 1, self); rb_obj_instance_eval(0, 0, options); params = rb_iv_get(options, "@params"); /* Check value type */ if(FIXNUM_P(id)) { int flags = 0, vid = -1; Pixmap stipple = None; VALUE top = Qnil, bottom = Qnil; /* Get options */ if(T_HASH == rb_type(params)) { if(!NIL_P(value = RubyValueToIcon(rb_hash_lookup(params, CHAR2SYM("stipple"))))) { flags |= SUB_SCREEN_STIPPLE; stipple = NUM2LONG(rb_iv_get(value, "@pixmap")); } if(T_ARRAY == rb_type(value = rb_hash_lookup(params, CHAR2SYM("top")))) { top = value; /// Lazy eval rb_ary_push(shelter, value); ///< Protect from GC } if(T_ARRAY == rb_type(value = rb_hash_lookup(params, CHAR2SYM("bottom")))) { bottom = value; ///< Lazy eval rb_ary_push(shelter, value); ///< Protect from GC } if(T_FIXNUM == rb_type(value = rb_hash_lookup(params, CHAR2SYM("view")))) vid = FIX2INT(value); } /* Skip on checking only */ if(!(subtle->flags & SUB_SUBTLE_CHECK)) { if((s = subArrayGet(subtle->screens, FIX2INT(id) - 1))) { s->flags |= (flags|SUB_RUBY_DATA); s->top = top; s->bottom = bottom; s->stipple = stipple; if(-1 != vid) s->viewid = vid; } } } else rb_raise(rb_eArgError, "Unknown value type for screen"); return Qnil; } /* }}} */ /* RubyConfigStyle {{{ */ /* * call-seq: style(name, blk) -> nil * * Add style values * * style :title do * foreground "#fecf35" * background "#202020" * end */ static VALUE RubyConfigStyle(VALUE self, VALUE name) { rb_need_block(); /* Check value type */ if(T_SYMBOL == rb_type(name)) { SubStyle *s = NULL; VALUE klass = Qnil, options = Qnil, styles = Qnil; /* Select style struct */ if(CHAR2SYM("all") == name) s = &subtle->styles.all; else if(CHAR2SYM("views") == name) s = &subtle->styles.views; else if(CHAR2SYM("title") == name) s = &subtle->styles.title; else if(CHAR2SYM("sublets") == name) s = &subtle->styles.sublets; else if(CHAR2SYM("separator") == name) s = &subtle->styles.separator; else if(CHAR2SYM("clients") == name) s = &subtle->styles.clients; else if(CHAR2SYM("subtle") == name) s = &subtle->styles.subtle; else { subSubtleLogWarn("Unexpected style name `:%s'\n", SYM2CHAR(name)); return Qnil; } if(subtle->flags & SUB_SUBTLE_CHECK) return Qnil; ///< Skip on check /* Collect options */ klass = rb_const_get(mod, rb_intern("Options")); options = rb_funcall(klass, rb_intern("new"), 1, self); rb_obj_instance_eval(0, 0, options); /* Eval style before styles */ RubyEvalStyle(name, s, rb_iv_get(options, "@params")); /* Eval styles */ if(T_HASH == rb_type((styles = rb_iv_get(options, "@styles")))) rb_hash_foreach(styles, RubyForeachStyle, (VALUE)s); } else rb_raise(rb_eArgError, "Unexpected value type for style `%s'", rb_obj_classname(name)); return Qnil; } /* }}} */ /* RubyConfigLoadConfig {{{ */ /* * call-seq: load_config(file) -> true or false * * Load config file * * load_config "gravities.rb" * => true */ static VALUE RubyConfigLoadConfig(VALUE self, VALUE file) { int state = 0; char buf[100] = { 0 }; VALUE rargs[2] = { Qnil }; /* Check if file exists otherwise try to find it */ if(-1 == access(RSTRING_PTR(file), R_OK)) { int len = 0; char *home = NULL, *dirs = NULL, *tok = NULL, tokens[200] = { 0 }, *tokensp = tokens; /* Combine XDG paths */ if((home = getenv("XDG_CONFIG_HOME"))) len += snprintf(tokens, sizeof(tokens), "%s", home); else len += snprintf(tokens, sizeof(tokens), "%s/.config", getenv("HOME")); if((dirs = getenv("XDG_CONFIG_DIRS"))) len += snprintf(tokens + len, sizeof(tokens), ":%s", dirs); else len += snprintf(tokens + len, sizeof(tokens), ":%s/%s", "/etc/xdg", PKG_NAME); if((home = getenv("XDG_DATA_HOME"))) { snprintf(buf, sizeof(buf), "%s:%s/%s/sublets", tokens, home, PKG_NAME); } else snprintf(buf, sizeof(buf), "%s:%s/.local/share/%s/sublets", tokens, getenv("HOME"), PKG_NAME); /* Search file in XDG paths */ while((tok = strsep(&tokensp, ":"))) { /* Check if config file exists in tok or tok/subtle */ snprintf(buf, sizeof(buf), "%s/%s", tok, RSTRING_PTR(file)); if(-1 != access(buf, R_OK)) break; else { snprintf(buf, sizeof(buf), "%s/%s/%s", tok, PKG_NAME, RSTRING_PTR(file)); if(-1 != access(buf, R_OK)) break; } } } else snprintf(buf, sizeof(buf), "%s", RSTRING_PTR(file)); printf("Reading file `%s'\n", buf); /* Carefully load and eval file */ rargs[0] = rb_str_new2(buf); rargs[1] = self; rb_protect(RubyWrapEvalFile, (VALUE)&rargs, &state); if(state) { subSubtleLogWarn("Cannot load file `%s'\n", buf); RubyBacktrace(); return Qfalse; } return Qtrue; } /* }}} */ /* Sublet */ /* RubySubletDispatcher {{{ */ /* * Dispatcher for Sublet instance variables - internal use only */ static VALUE RubySubletDispatcher(int argc, VALUE *argv, VALUE self) { VALUE missing = Qnil, args = Qnil, ret = Qnil; rb_scan_args(argc, argv, "1*", &missing, &args); /* Dispatch calls */ if(rb_respond_to(self, rb_to_id(missing))) ///< Intance methods ret = rb_funcall2(self, rb_to_id(missing), --argc, ++argv); else ///< Instance variables { char buf[20] = { 0 }; char *name = (char *)rb_id2name(SYM2ID(missing)); snprintf(buf, sizeof(buf), "@%s", name); if(index(name, '=')) { int len = 0; VALUE value = Qnil; value = rb_ary_entry(args, 0); ///< Get first arg len = strlen(name); buf[len] = '\0'; ///< Overwrite equal sign rb_funcall(self, rb_intern("instance_variable_set"), 2, CHAR2SYM(buf), value); } else ret = rb_funcall(self, rb_intern("instance_variable_get"), 1, CHAR2SYM(buf)); } return ret; } /* }}} */ /* RubySubletConfig {{{ */ /* * call-seq: config -> Hash * * Get config hash from config * * s.config * => { :interval => 30 } */ static VALUE RubySubletConfig(VALUE self) { SubPanel *p = NULL; VALUE hash = Qnil; Data_Get_Struct(self, SubPanel, p); if(p) { /* Get config hash */ if(NIL_P(hash = rb_hash_lookup(config_sublets, CHAR2SYM(p->sublet->name)))) hash = rb_hash_new(); } return hash; } /* }}} */ /* RubySubletConfigure {{{ */ /* * call-seq: configure -> nil * * Configure block for Sublet * * configure :sublet do |s| * s.interval = 60 * end */ static VALUE RubySubletConfigure(VALUE self, VALUE name) { rb_need_block(); /* Check value type */ if(T_SYMBOL == rb_type(name)) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { VALUE proc = Qnil; /* Assume latest sublet */ proc = rb_block_proc(); p->sublet->name = strdup(SYM2CHAR(name)); /* Define configure method */ rb_funcall(rb_singleton_class(p->sublet->instance), rb_intern("define_method"), 2, CHAR2SYM("__configure"), proc); } } else rb_raise(rb_eArgError, "Unknown value type for configure"); return Qnil; } /* }}} */ /* RubySubletHelper {{{ */ /* * call-seq: helper -> nil * * Helper block for Sublet * * helper do |s| * def test * puts "test" * end * end */ static VALUE RubySubletHelper(VALUE self) { SubPanel *p = NULL; rb_need_block(); Data_Get_Struct(self, SubPanel, p); if(p) { /* Instance eval the block */ rb_yield_values(1, p->sublet->instance); rb_obj_instance_eval(0, 0, p->sublet->instance); } return Qnil; } /* }}} */ /* RubySubletOn {{{ */ /* * call-seq: on(event, &block) -> nil * * Event block for hooks * * on :event do |s| * puts s.name * end */ static VALUE RubySubletOn(VALUE self, VALUE event) { /* Check value type */ if(T_SYMBOL == rb_type(event) && rb_block_given_p()) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { char buf[64] = { 0 }; int i, arity = 0; VALUE proc = Qnil, sing = Qnil, meth = Qnil; RubyMethods methods[] = { { CHAR2SYM("run"), CHAR2SYM("__run"), SUB_SUBLET_RUN, 1 }, { CHAR2SYM("data"), CHAR2SYM("__data"), SUB_SUBLET_DATA, 2 }, { CHAR2SYM("watch"), CHAR2SYM("__watch"), SUB_SUBLET_WATCH, 1 }, { CHAR2SYM("unload"), CHAR2SYM("__unload"), SUB_SUBLET_UNLOAD, 1 }, { CHAR2SYM("mouse_down"), CHAR2SYM("__down"), SUB_PANEL_DOWN, 4 }, { CHAR2SYM("mouse_over"), CHAR2SYM("__over"), SUB_PANEL_OVER, 1 }, { CHAR2SYM("mouse_out"), CHAR2SYM("__out"), SUB_PANEL_OUT, 1 } }; /* Collect stuff */ proc = rb_block_proc(); arity = rb_proc_arity(proc); sing = rb_singleton_class(p->sublet->instance); meth = rb_intern("define_method"); /* Special hooks */ for(i = 0; LENGTH(methods) > i; i++) { if(methods[i].sym == event) { /* Check proc arity */ if(-1 == arity || (1 <= arity && methods[i].arity >= arity)) { /* Add flags */ if(methods[i].flags & (SUB_PANEL_DOWN| SUB_PANEL_OVER|SUB_PANEL_OUT)) p->flags |= methods[i].flags; else p->sublet->flags |= methods[i].flags; /* Create instance method from proc */ rb_funcall(sing, meth, 2, methods[i].real, proc); return Qnil; } else rb_raise(rb_eArgError, "Wrong number of arguments (%d for %d)", arity, methods[i].arity); } } /* Generic hooks */ snprintf(buf, sizeof(buf), "__hook_%s", SYM2CHAR(event)); rb_funcall(sing, meth, 2, CHAR2SYM(buf), proc); RubyEvalHook(event, rb_obj_method(p->sublet->instance, CHAR2SYM(buf))); } } else rb_raise(rb_eArgError, "Unknown value type for on"); return Qnil; } /* }}} */ /* RubySubletGrab {{{ */ /* * call-seq: grab(chain, &block) -> nil * * Add grabs to sublets * * grab "A-b" do |s| * puts s.name * end */ static VALUE RubySubletGrab(VALUE self, VALUE name) { /* Check value type */ if(T_SYMBOL == rb_type(name) && rb_block_given_p()) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { int i; char buf[64] = { 0 }; VALUE meth = Qnil; /* Add proc as instance method */ snprintf(buf, sizeof(buf), "__grab_%s", SYM2CHAR(name)); rb_funcall(rb_singleton_class(p->sublet->instance), rb_intern("define_method"), 2, CHAR2SYM(buf), rb_block_proc()); meth = rb_obj_method(p->sublet->instance, CHAR2SYM(buf)); /* Find grabs with this symbol */ for(i = 0; i < subtle->grabs->ndata; i++) { SubGrab *g = GRAB(subtle->grabs->data[i]); if(g->flags & SUB_RUBY_DATA && g->data.num == name) { g->flags ^= (SUB_RUBY_DATA|SUB_GRAB_PROC); g->data.num = (unsigned long)meth; rb_ary_push(shelter, meth); ///< Protect from GC } } } } else rb_raise(rb_eArgError, "Unknown value type for grab"); return Qnil; } /* }}} */ /* RubySubletRender {{{ */ /* * call-seq: render -> nil * * Render a Sublet * * sublet.render * => nil */ static VALUE RubySubletRender(VALUE self) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) subScreenRender(); return Qnil; } /* }}} */ /* RubySubletNameReader {{{ */ /* * call-seq: name -> String * * Get name of Sublet * * puts sublet.name * => "sublet" */ static VALUE RubySubletNameReader(VALUE self) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); return p ? rb_str_new2(p->sublet->name) : Qnil; } /* }}} */ /* RubySubletIntervalReader {{{ */ /* * call-seq: interval -> Fixnum * * Get interval time of Sublet * * puts sublet.interval * => 60 */ static VALUE RubySubletIntervalReader(VALUE self) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); return p ? INT2FIX(p->sublet->interval) : Qnil; } /* }}} */ /* RubySubletIntervalWriter {{{ */ /* * call-seq: interval=(fixnum) -> nil * * Set interval time of Sublet * * sublet.interval = 60 * => nil */ static VALUE RubySubletIntervalWriter(VALUE self, VALUE value) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { if(FIXNUM_P(value)) { p->sublet->interval = FIX2INT(value); p->sublet->time = subSubtleTime() + p->sublet->interval; if(0 < p->sublet->interval) p->sublet->flags |= SUB_SUBLET_INTERVAL; else p->sublet->flags &= ~SUB_SUBLET_INTERVAL; } else rb_raise(rb_eArgError, "Unknown value type `%s'", rb_obj_classname(value)); } return Qnil; } /* }}} */ /* RubySubletDataReader {{{ */ /* * call-seq: data -> String or nil * * Get data of Sublet * * puts sublet.data * => "subtle" */ static VALUE RubySubletDataReader(VALUE self) { int i; VALUE string = Qnil; SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { if(0 < p->sublet->text->nitems) { /* Concat string */ for(i = 0; i < p->sublet->text->nitems; i++) { SubTextItem *item = (SubTextItem *)p->sublet->text->items[i]; if(Qnil == string) rb_str_new2(item->data.string); else rb_str_cat(string, item->data.string, strlen(item->data.string)); } } } return string; } /* }}} */ /* RubySubletDataWriter {{{ */ /* * call-seq: data=(string) -> nil * * Set data of Sublet * * sublet.data = "subtle" * => nil */ static VALUE RubySubletDataWriter(VALUE self, VALUE value) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { /* Check value type */ if(T_STRING == rb_type(value)) { SubStyle *s = &subtle->styles.sublets, *style = NULL; /* Select style */ if(s->styles && (style = subArrayGet(s->styles, p->sublet->styleid))) s = style; p->sublet->width = subTextParse(p->sublet->text, subtle->styles.sublets.font, RSTRING_PTR(value)) + STYLE_WIDTH((*s)); } else rb_raise(rb_eArgError, "Unknown value type"); } return Qnil; } /* }}} */ /* RubySubletGeometryReader {{{ */ /* * call-seq: geometry -> Subtlext::Geometry * * Get geometry of a sublet * * sublet.geometry * => # */ VALUE RubySubletGeometryReader(VALUE self) { SubPanel *p = NULL; VALUE geometry = Qnil; Data_Get_Struct(self, SubPanel, p); if(p) { SubStyle *s = NULL; XRectangle geom = { 0 }; VALUE subtlext = Qnil, klass = Qnil; /* Pick sublet style */ if(subtle->styles.sublets.styles) s = subArrayGet(subtle->styles.sublets.styles, p->sublet->styleid); subPanelGeometry(p, s ? s : &subtle->styles.sublets, &geom); /* Create geometry object */ subtlext = rb_const_get(rb_mKernel, rb_intern("Subtlext")); klass = rb_const_get(subtlext, rb_intern("Geometry")); geometry = rb_funcall(klass, rb_intern("new"), 4, INT2FIX(geom.x), INT2FIX(geom.y), INT2FIX(geom.width), INT2FIX(geom.height)); } return geometry; } /* }}} */ /* RubySubletScreenReader {{{ */ /* * call-seq: screen -> Subtlext::Screen * * Get screen of a sublet * * sublet.screen * => # */ VALUE RubySubletScreenReader(VALUE self) { SubPanel *p = NULL; VALUE screen = Qnil; Data_Get_Struct(self, SubPanel, p); if(p) screen = RubySubtleToSubtlext(p->screen); return screen; } /* }}} */ /* RubySubletStyleWriter {{{ */ /* * call-seq: style=(string) -> nil * style=(symbol) -> nil * * Set style of Sublet * * sublet.style = :subtle * => nil */ static VALUE RubySubletStyleWriter(VALUE self, VALUE value) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { /* Check value type */ if(FIXNUM_P(value)) { SubStyle *s = &subtle->styles.sublets, *style = NULL; /* Select style */ if(s->styles && (style = subArrayGet(s->styles, FIX2INT(value)))) { s = style; p->sublet->styleid = FIX2INT(value); } p->sublet->width = subTextParse(p->sublet->text, subtle->styles.sublets.font, RSTRING_PTR(value)) + STYLE_WIDTH((*s)); } else rb_raise(rb_eArgError, "Unknown value type"); } return Qnil; } /* }}} */ /* RubySubletShow {{{ */ /* * call-seq: show -> nil * * Show sublet on panel * * sublet.show * => nil */ static VALUE RubySubletShow(VALUE self) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { p->flags &= ~SUB_PANEL_HIDDEN; /* Update screens */ subScreenUpdate(); subScreenRender(); } return Qnil; } /* }}} */ /* RubySubletHide {{{ */ /* * call-seq: hide -> nil * * Hide sublet from panel * * sublet.hide * => nil */ static VALUE RubySubletHide(VALUE self, VALUE value) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { p->flags |= SUB_PANEL_HIDDEN; /* Update screens */ subScreenUpdate(); subScreenRender(); } return Qnil; } /* }}} */ /* RubySubletWatch {{{ */ /* * call-seq: watch(source) -> true or false * * Add watch file via inotify or socket * * watch "/path/to/file" * => true * * @socket = TCPSocket("localhost", 6600) * watch @socket */ static VALUE RubySubletWatch(VALUE self, VALUE value) { VALUE ret = Qfalse; SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { if(!(p->sublet->flags & (SUB_SUBLET_SOCKET|SUB_SUBLET_INOTIFY)) && RTEST(value)) { /* Socket file descriptor or ruby socket */ if(FIXNUM_P(value) || rb_respond_to(value, rb_intern("fileno"))) { int flags = 0; p->sublet->flags |= SUB_SUBLET_SOCKET; /* Get socket file descriptor */ if(FIXNUM_P(value)) p->sublet->watch = FIX2INT(value); else { p->sublet->watch = FIX2INT(rb_funcall(value, rb_intern("fileno"), 0, NULL)); } XSaveContext(subtle->dpy, subtle->windows.support, p->sublet->watch, (void *)p); subEventWatchAdd(p->sublet->watch); /* Set nonblocking */ if(-1 == (flags = fcntl(p->sublet->watch, F_GETFL, 0))) flags = 0; fcntl(p->sublet->watch, F_SETFL, flags | O_NONBLOCK); ret = Qtrue; } #ifdef HAVE_SYS_INOTIFY_H else if(T_STRING == rb_type(value)) /// Inotify file { char buf[100] = { 0 }; #ifdef HAVE_WORDEXP_H /* Expand tildes in path */ wordexp_t we; if(0 == wordexp(RSTRING_PTR(value), &we, 0)) { snprintf(buf, sizeof(buf), "%s", we.we_wordv[0]); wordfree(&we); } else #endif /* HAVE_WORDEXP_H */ snprintf(buf, sizeof(buf), "%s", RSTRING_PTR(value)); /* Create inotify watch */ if(0 < (p->sublet->watch = inotify_add_watch( subtle->notify, buf, IN_MODIFY))) { p->sublet->flags |= SUB_SUBLET_INOTIFY; XSaveContext(subtle->dpy, subtle->windows.support, p->sublet->watch, (void *)p); subSubtleLogDebug("Inotify: add watch=%s\n", buf); ret = Qtrue; } else subSubtleLogWarn("Cannot watch file `%s': %s\n", buf, strerror(errno)); } #endif /* HAVE_SYS_INOTIFY_H */ else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } } return ret; } /* }}} */ /* RubySubletUnwatch {{{ */ /* * call-seq: unwatch -> true or false * * Remove watch from Sublet * * unwatch * => true */ static VALUE RubySubletUnwatch(VALUE self) { VALUE ret = Qfalse; SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p) { /* Probably a socket */ if(p->sublet->flags & SUB_SUBLET_SOCKET) { XDeleteContext(subtle->dpy, subtle->windows.support, p->sublet->watch); subEventWatchDel(p->sublet->watch); p->sublet->flags &= ~SUB_SUBLET_SOCKET; p->sublet->watch = 0; ret = Qtrue; } #ifdef HAVE_SYS_INOTIFY_H /* Inotify file */ else if(p->sublet->flags & SUB_SUBLET_INOTIFY) { subSubtleLogDebug("Inotify: remove watch=%d\n", p->sublet->watch); XDeleteContext(subtle->dpy, subtle->windows.support, p->sublet->watch); inotify_rm_watch(subtle->notify, p->sublet->watch); p->sublet->flags &= ~SUB_SUBLET_INOTIFY; p->sublet->watch = 0; ret = Qtrue; } #endif /* HAVE_SYS_INOTIFY_H */ } return ret; } /* }}} */ /* RubySubletWarn {{{ */ /* * call-seq: warn(string) -> nil * * Print sublet warning * * sublet.warn("test") * => " test" */ static VALUE RubySubletWarn(VALUE self, VALUE str) { SubPanel *p = NULL; Data_Get_Struct(self, SubPanel, p); if(p && T_STRING == rb_type(str)) subSubtleLogSubletError(p->sublet->name, RSTRING_PTR(str)); return Qnil; } /* }}} */ /* Public */ /** subRubyInit {{{ * @brief Init ruby **/ void subRubyInit(void) { VALUE config = Qnil, options = Qnil, sublet = Qnil; RUBY_INIT_STACK; ruby_init(); ruby_init_loadpath(); ruby_script("subtle"); #ifdef HAVE_RB_ENC_SET_DEFAULT_INTERNAL { VALUE encoding = Qnil; /* FIXME: Fix for ruby 1.9.2p429 borrowed from ruby? */ (void)rb_filesystem_encoding(); /* Set encoding */ encoding = rb_enc_from_encoding(rb_locale_encoding()); rb_enc_set_default_external(encoding); } #endif /* HAVE_RB_ENC_SET_DEFAULT_INTERNAL */ /* FIXME: Init ruby gems */ { char *opts[] = { "ruby", "-e;" }; /* Fake ruby_init_prelude() */ ruby_options(2, opts); } /* FIXME: Autload seems to be broken in <1.9.2, use dispatcher */ //rb_autoload(rb_cObject, SYM2ID(CHAR2SYM("Subtlext")), "subtle/subtlext"); /* * Document-class: Object * * Ruby Object class dispatcher */ rb_define_singleton_method(rb_cObject, "const_missing", RubyObjectDispatcher, 1); /* * Document-class: Subtle * * Subtle is the module for interaction with the window manager */ mod = rb_define_module("Subtle"); /* * Document-class: Config * * Config class for DSL evaluation */ config = rb_define_class_under(mod, "Config", rb_cObject); /* Class methods */ rb_define_method(config, "method_missing", RubyConfigMissing, -1); rb_define_method(config, "singleton_method_added", RubyConfigAdded, 1); rb_define_method(config, "set", RubyConfigSet, 2); rb_define_method(config, "gravity", RubyConfigGravity, -1); rb_define_method(config, "grab", RubyConfigGrab, -1); rb_define_method(config, "tag", RubyConfigTag, -1); rb_define_method(config, "view", RubyConfigView, -1); rb_define_method(config, "on", RubyConfigOn, -1); rb_define_method(config, "sublet", RubyConfigSublet, 1); rb_define_method(config, "screen", RubyConfigScreen, 1); rb_define_method(config, "style", RubyConfigStyle, 1); rb_define_method(config, "load_config", RubyConfigLoadConfig, 1); /* * Document-class: Options * * Options class for DSL evaluation */ options = rb_define_class_under(mod, "Options", rb_cObject); /* Params list */ rb_define_attr(options, "params", 1, 1); rb_define_attr(options, "styles", 1, 1); /* Class methods */ rb_define_method(options, "initialize", RubyOptionsInit, 1); rb_define_method(options, "match", RubyOptionsMatch, 1); rb_define_method(options, "gravity", RubyOptionsGravity, 1); rb_define_method(options, "style", RubyOptionsStyle, 1); rb_define_method(options, "on_match", RubyOptionsOnMatch, -1); rb_define_method(options, "method_missing", RubyOptionsDispatcher, -1); /* * Document-class: Subtle::Sublet * * Sublet class for interaction with sublets */ sublet = rb_define_class_under(mod, "Sublet", rb_cObject); /* DSL stuff */ rb_define_method(sublet, "configure", RubySubletConfigure, 1); rb_define_method(sublet, "helper", RubySubletHelper, 0); rb_define_method(sublet, "on", RubySubletOn, 1); rb_define_method(sublet, "grab", RubySubletGrab, 1); /* Class methods */ rb_define_method(sublet, "method_missing", RubySubletDispatcher, -1); rb_define_method(sublet, "config", RubySubletConfig, 0); rb_define_method(sublet, "render", RubySubletRender, 0); rb_define_method(sublet, "name", RubySubletNameReader, 0); rb_define_method(sublet, "interval", RubySubletIntervalReader, 0); rb_define_method(sublet, "interval=", RubySubletIntervalWriter, 1); rb_define_method(sublet, "data", RubySubletDataReader, 0); rb_define_method(sublet, "data=", RubySubletDataWriter, 1); rb_define_method(sublet, "geometry", RubySubletGeometryReader, 0); rb_define_method(sublet, "screen", RubySubletScreenReader, 0); rb_define_method(sublet, "show", RubySubletShow, 0); rb_define_method(sublet, "style=", RubySubletStyleWriter, 1); rb_define_method(sublet, "hide", RubySubletHide, 0); rb_define_method(sublet, "watch", RubySubletWatch, 1); rb_define_method(sublet, "unwatch", RubySubletUnwatch, 0); rb_define_method(sublet, "warn", RubySubletWarn, 1); /* Bypassing garbage collection */ shelter = rb_ary_new(); rb_gc_register_address(&shelter); subSubtleLogDebugSubtle("Init\n"); } /* }}} */ /** subRubyLoadConfig {{{ * @brief Load config file * @param[in] path Path to config file **/ void subRubyLoadConfig(void) { VALUE klass = Qnil; SubTag *t = NULL; /* Create default tag */ if(!(subtle->flags & SUB_SUBTLE_CHECK) && (t = subTagNew("default", NULL))) subArrayPush(subtle->tags, (void *)t); /* Reset styles */ subStyleReset(&subtle->styles.all, 0); ///< Ensure sane base values subStyleReset(&subtle->styles.views, -1); subStyleReset(&subtle->styles.title, -1); subStyleReset(&subtle->styles.sublets, -1); subStyleReset(&subtle->styles.separator, -1); subStyleReset(&subtle->styles.clients, 0); subStyleReset(&subtle->styles.subtle, 0); /* Reset values */ subtle->gravity = -1; subtle->styles.subtle.bg = -1; ///< Must be -1 for wallpaper subtle->styles.urgent = NULL; subtle->styles.occupied = NULL; subtle->styles.focus = NULL; subtle->styles.viewsep = NULL; subtle->styles.subletsep = NULL; /* Create and register config values */ config_sublets = rb_hash_new(); config_methods = rb_ary_new(); rb_gc_register_address(&config_sublets); rb_gc_register_address(&config_methods); /* Load supplied config or default */ klass = rb_const_get(mod, rb_intern("Config")); config_instance = rb_funcall(klass, rb_intern("new"), 0, NULL); rb_gc_register_address(&config_instance); if(Qfalse == RubyConfigLoadConfig(config_instance, rb_str_new2(subtle->paths.config ? subtle->paths.config : PKG_CONFIG))) { subSubtleFinish(); exit(-1); } else if(subtle->flags & SUB_SUBTLE_CHECK) printf("Syntax OK\n"); /* If not check only, lazy eval config values */ if(!(subtle->flags & SUB_SUBTLE_CHECK)) RubyEvalConfig(); /* Release methods list */ rb_gc_unregister_address(&config_methods); return; } /* }}} */ /** subRubyReloadConfig {{{ * @brief Reload config file **/ void subRubyReloadConfig(void) { int i, rx = 0, ry = 0, x = 0, y = 0, *vids = NULL; unsigned int mask = 0; Window root = None, win = None; SubClient *c = NULL; /* Reset panel height */ subtle->ph = 0; /* Reset flags before reloading */ subtle->flags &= (SUB_SUBTLE_DEBUG|SUB_SUBTLE_EWMH|SUB_SUBTLE_RUN| SUB_SUBTLE_XINERAMA|SUB_SUBTLE_XRANDR|SUB_SUBTLE_URGENT); /* Unregister config values */ rb_gc_unregister_address(&config_sublets); rb_gc_unregister_address(&config_instance); rb_gc_unregister_address(&config_methods); /* Reset sublet panel flags */ for(i = 0; i < subtle->sublets->ndata; i++) { SubPanel *p = PANEL(subtle->sublets->data[i]); p->flags &= ~(SUB_PANEL_BOTTOM|SUB_PANEL_SPACER1| SUB_PANEL_SPACER1| SUB_PANEL_SEPARATOR1|SUB_PANEL_SEPARATOR2); p->screen = NULL; } /* Allocate memory to store current views per screen */ vids = (int *)subSharedMemoryAlloc(subtle->screens->ndata, sizeof(int)); /* Reset screen panels */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); vids[i] = s->viewid; ///< Store views s->flags &= ~(SUB_SCREEN_STIPPLE|SUB_SCREEN_PANEL1|SUB_SCREEN_PANEL2); subArrayClear(s->panels, True); } /* Clear arrays */ subArrayClear(subtle->hooks, True); ///< Must be first subArrayClear(subtle->grabs, True); subArrayClear(subtle->gravities, True); subArrayClear(subtle->sublets, False); subArrayClear(subtle->tags, True); subArrayClear(subtle->views, True); /* Load and configure */ subRubyLoadConfig(); subRubyLoadSublets(); subRubyLoadPanels(); subDisplayConfigure(); /* Restore current views */ for(i = 0; i < subtle->screens->ndata; i++) { if(vids[i] < subtle->views->ndata) SCREEN(subtle->screens->data[i])->viewid = vids[i]; } /* Update client tags */ for(i = 0; i < subtle->clients->ndata; i++) { int flags = 0; c = CLIENT(subtle->clients->data[i]); c->gravityid = -1; c->flags = (c->flags & (SUB_TYPE_CLIENT|SUB_CLIENT_FOCUS| SUB_CLIENT_INPUT|SUB_CLIENT_CLOSE)); ///< Reset flags subClientSetType(c, &flags); subClientRetag(c, &flags); subClientToggle(c, ~c->flags & flags, True); ///< Toggle flags } printf("Reloaded config\n"); /* Update screens and panels */ subScreenConfigure(); subScreenUpdate(); subScreenRender(); subPanelPublish(); /* Focus pointer window */ XQueryPointer(subtle->dpy, ROOT, &root, &win, &rx, &ry, &x, &y, &mask); if((c = CLIENT(subSubtleFind(win, CLIENTID)))) subClientFocus(c, True); else { c = subClientNext(0, False); if(c) subClientFocus(c, True); } /* Hook: Reload */ subHookCall(SUB_HOOK_RELOAD, NULL); free(vids); } /* }}} */ /** subRubyLoadSublet {{{ * @brief Load sublets from path * @param[in] path Path of the sublets **/ void subRubyLoadSublet(const char *file) { int state = 0; SubPanel *p = NULL; VALUE rargs[3] = { Qnil }; /* Load sublet */ p = subPanelNew(SUB_PANEL_SUBLET); p->sublet->instance = Data_Wrap_Struct(rb_const_get(mod, rb_intern("Sublet")), NULL, NULL, (void *)p); rb_ary_push(shelter, p->sublet->instance); ///< Protect from GC if(Qfalse == RubyConfigLoadConfig(p->sublet->instance, rb_str_new2(file))) { subSubtleLogWarn("Cannot load sublet `%s'\n", file); RubyBacktrace(); subRubyUnloadSublet(p); return; } /* Carefully apply sublet config */ rargs[0] = CHAR2SYM(p->sublet->name); rargs[1] = (VALUE)p->sublet; rb_protect(RubyWrapSubletConfig, (VALUE)&rargs, &state); if(state) { subSubtleLogWarn("Cannot configure sublet `%s'\n", file); RubyBacktrace(); subRubyUnloadSublet(p); return; } /* Carefully configure sublet */ if(!subRubyCall(SUB_CALL_CONFIGURE, p->sublet->instance, NULL)) { subRubyUnloadSublet(p); return; } /* Sanitize interval time */ if(0 >= p->sublet->interval) p->sublet->interval = 60; /* First run */ if(p->sublet->flags & SUB_SUBLET_RUN) subRubyCall(SUB_CALL_RUN, p->sublet->instance, NULL); subArrayPush(subtle->sublets, (void *)p); printf("Loaded sublet (%s)\n", p->sublet->name); } /* }}} */ /** subRubyUnloadSublet {{{ * @brief Unload sublets at runtime * @param[in] p A #SubPanel **/ void subRubyUnloadSublet(SubPanel *p) { int i; assert(p); /* Remove sublet from panels */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); if(s->panels) subArrayRemove(s->panels, (void *)p); } /* Remove hooks */ for(i = 0; i < subtle->hooks->ndata; i++) { SubHook *hook = HOOK(subtle->hooks->data[i]); if(RubyReceiver(p->sublet->instance, hook->proc)) { subArrayRemove(subtle->hooks, (void *)hook); subRubyRelease(hook->proc); subHookKill(hook); i--; ///< Prevent skipping of entries } } /* Remove grabs */ for(i = 0; i < subtle->grabs->ndata; i++) { SubGrab *grab = GRAB(subtle->grabs->data[i]); if(grab->flags & SUB_GRAB_PROC && RubyReceiver(p->sublet->instance, grab->data.num)) { subArrayRemove(subtle->grabs, (void *)grab); subRubyRelease(grab->data.num); subGrabKill(grab); i--; ///< Prevent skipping of entries } } subArrayRemove(subtle->sublets, (void *)p); subPanelKill(p); subPanelPublish(); } /* }}} */ /** subRubyLoadPanels {{{ * @brief Load panels **/ void subRubyLoadPanels(void) { int state = 0; /* Carefully load panels */ rb_protect(RubyWrapLoadPanels, Qnil, &state); if(state) { subSubtleLogWarn("Cannot load panel config\n"); RubyBacktrace(); subSubtleFinish(); exit(-1); } } /* }}} */ /** subRubyLoadSublets {{{ * @brief Load sublets from path **/ void subRubyLoadSublets(void) { int i, num, len = 0; char buf[100] = { 0 }; struct dirent **entries = NULL; #ifdef HAVE_SYS_INOTIFY_H /* Init inotify on demand */ if(!subtle->notify) { if(0 > (subtle->notify = inotify_init())) { subSubtleLogWarn("Cannot init inotify\n"); subSubtleLogDebug("Inotify: error=%s\n", strerror(errno)); return; } else fcntl(subtle->notify, F_SETFL, O_NONBLOCK); } #endif /* HAVE_SYS_INOTIFY_H */ /* Check path */ if(subtle->paths.sublets) len += snprintf(buf, sizeof(buf), "%s", subtle->paths.sublets); else { char *home = NULL; if((home = getenv("XDG_DATA_HOME"))) { len += snprintf(buf, sizeof(buf), "%s/%s/sublets", home, PKG_NAME); } else len += snprintf(buf, sizeof(buf), "%s/.local/share/%s/sublets", getenv("HOME"), PKG_NAME); } /* Scan directory */ if(0 < ((num = scandir(buf, &entries, RubyFilter, alphasort)))) { for(i = 0; i < num; i++) { /* Temporary append file name to path */ snprintf(buf + len, sizeof(buf), "/%s", entries[i]->d_name); subRubyLoadSublet(buf); /* Restore path */ buf[strlen(buf) - (strlen(entries[i]->d_name) + 1)] = '\0'; free(entries[i]); } free(entries); subArraySort(subtle->grabs, subGrabCompare); } } /* }}} */ /** subRubyCall {{{ * @brief Safely call ruby script * @param[in] type Script type * @param[in] proc Script receiver * @param[in] data Extra data * @retval 1 Call was successful * @retval 0 Call failed **/ int subRubyCall(int type, unsigned long proc, void *data) { int state = 0; VALUE rargs[3] = { Qnil }; /* Wrap up data */ rargs[0] = (VALUE)type; rargs[1] = proc; rargs[2] = (VALUE)data; /* Carefully call */ rb_protect(RubyWrapCall, (VALUE)&rargs, &state); if(state) RubyBacktrace(); #ifdef DEBUG subSubtleLogDebugRuby("Call: GC START\n"); rb_gc_start(); #endif /* DEBUG */ return !state; ///< Reverse odd logic } /* }}} */ /** subRubyRelease {{{ * @brief Release value from shelter * @param[in] value The released value **/ int subRubyRelease(unsigned long value) { int state = 0; rb_protect(RubyWrapRelease, value, &state); return state; } /* }}} */ /** subRubyFinish {{{ * @brief Finish ruby stack **/ void subRubyFinish(void) { if(Qnil != shelter) { ruby_finalize(); #ifdef HAVE_SYS_INOTIFY_H if(subtle && subtle->notify) close(subtle->notify); #endif /* HAVE_SYS_INOTIFY_H */ } subSubtleLogDebugSubtle("Finish\n"); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/subtle.h0000644000175000017500000015501711770332063017757 0ustar formorerformorer /** * @package subtle * * @file Header file * @copyright Copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/subtle.h,v 3208 2012/05/22 23:43:43 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #ifndef SUBTLE_H #define SUBTLE_H 1 /* Includes {{{ */ #include #include #include #include #include #include #include #include #include "config.h" #include "shared.h" #ifdef HAVE_SYS_INOTIFY_H #include #endif /* HAVE_SYS_INOTIFY */ #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H #include #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */ #ifdef HAVE_X11_EXTENSIONS_XRANDR_H #include #endif /* HAVE_X11_EXTENSIONS_XRANDR_H */ /* }}} */ /* Macros {{{ */ #define FLAGS unsigned int ///< Flags #define TAGS unsigned int ///< Tags #define CLIENTID 1L ///< Client data id #define TRAYID 2L ///< Tray data id #define SCREENID 3L ///< Screen data id #define MINW 1L ///< Client min width #define MINH 1L ///< Client min height #define WAITTIME 10 ///< Max waiting time #define HISTORYSIZE 5 ///< Size of the focus history #define DEFAULTTAG (1L << 1) ///< Default tag #define GRAVITYSTRLIMIT 1 ///< Gravity string limit to ignore \0 #define DEFAULT_LOGLEVEL \ (SUB_LOG_WARN|SUB_LOG_ERROR|SUB_LOG_SUBLET| \ SUB_LOG_DEPRECATED) ///< Default loglevel #define DEBUG_LOGLEVEL \ (SUB_LOG_EVENTS|SUB_LOG_RUBY|SUB_LOG_XERROR| \ SUB_LOG_SUBTLE|SUB_LOG_DEBUG) ///< Debug loglevel #define BORDER(C) \ (C->flags & SUB_CLIENT_MODE_BORDERLESS ? 0 : \ subtle->styles.clients.border.top) ///< Get border width #define STYLE_TOP(S) \ (S.border.top + S.padding.top + S.margin.top) ///< Get style top #define STYLE_RIGHT(S) \ (S.border.right + S.padding.right + S.margin.right) ///< Get style right #define STYLE_BOTTOM(S) \ (S.border.bottom + S.padding.bottom + S.margin.bottom) ///< Get style bottom #define STYLE_LEFT(S) \ (S.border.left + S.padding.left + S.margin.left) ///< Get style left #define STYLE_WIDTH(S) (STYLE_LEFT(S) + STYLE_RIGHT(S)) ///< Get style width #define STYLE_HEIGHT(S) (STYLE_TOP(S) + STYLE_BOTTOM(S)) ///< Get style height #define STYLE_FLAG(Flag) (1L << (10 + Flag)) ///< Get style flag #define MIN(A,B) (A >= B ? B : A) ///< Minimum #define MAX(A,B) (A >= B ? A : B) ///< Maximum #define ALIVE(C) (C && !(C->flags & SUB_CLIENT_DEAD)) ///< Check if client is alive #define DEAD(C) \ if(!C || C->flags & SUB_CLIENT_DEAD) return; ///< Check dead clients #define MINMAX(Val,Min,Max) \ ((Val < Min) ? Min : ((Val > Max) ? Max : Val)) ///< Value min/max #define XYINRECT(Wx,Wy,R) \ (Wx >= R.x && Wx <= (R.x + R.width) && \ Wy >= R.y && Wy <= (R.y + R.height)) ///< Whether x/y is in rect #define VISIBLE(C) VISIBLETAGS(C,subtle->visible_tags) ///< Whether client is visible #define VISIBLETAGS(C,Tags) \ (C && (Tags & c->tags || \ C->flags & (SUB_CLIENT_TYPE_DESKTOP|SUB_CLIENT_MODE_STICK))) ///< Whether client is visible on tags #define ROOT DefaultRootWindow(subtle->dpy) ///< Root window #define SCRN DefaultScreen(subtle->dpy) ///< Default screen /* Logging macros */ #define subSubtleLogError(...) \ subSubtleLog(SUB_LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__); #define subSubtleLogSubletError(SUBLET, ...) \ subSubtleLog(SUB_LOG_SUBLET, SUBLET, __LINE__, __VA_ARGS__); #define subSubtleLogWarn(...) \ subSubtleLog(SUB_LOG_WARN, __FILE__, __LINE__, __VA_ARGS__); #define subSubtleLogDeprecated(...) \ subSubtleLog(SUB_LOG_DEPRECATED, __FILE__, __LINE__, __VA_ARGS__); #ifdef DEBUG #define subSubtleLogDebugEvents(...) \ subSubtleLog(SUB_LOG_EVENTS, __FILE__, __LINE__, __VA_ARGS__); #define subSubtleLogDebugRuby(...) \ subSubtleLog(SUB_LOG_RUBY, __FILE__, __LINE__, __VA_ARGS__); #define subSubtleLogDebugSubtlext(...) \ subSubtleLog(SUB_LOG_SUBTLEXT, __FILE__, __LINE__, __VA_ARGS__); #define subSubtleLogDebugSubtle(...) \ subSubtleLog(SUB_LOG_SUBTLE, __FILE__, __LINE__, __VA_ARGS__); #define subSubtleLogDebug(...) \ subSubtleLog(SUB_LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__); #else /* DEBUG */ #define subSubtleLogDebugEvents(...) #define subSubtleLogDebugRuby(...) #define subSubtleLogDebugSubtlext(...) #define subSubtleLogDebugSubtle(...) #define subSubtleLogDebug(...) #endif /* DEBUG */ /* }}} */ /* Masks {{{ */ #define ROOTMASK \ (StructureNotifyMask|SubstructureNotifyMask|\ SubstructureRedirectMask|PropertyChangeMask) #define CLIENTMASK \ (PropertyChangeMask|EnterWindowMask|FocusChangeMask) #define TRAYMASK (StructureNotifyMask|CLIENTMASK) #define DRAGMASK \ (PointerMotionMask|ButtonReleaseMask|KeyPressMask| \ EnterWindowMask|FocusChangeMask) #define GRABMASK \ (ButtonPressMask|ButtonReleaseMask|PointerMotionMask) /* }}} */ /* Casts {{{ */ #define ARRAY(a) ((SubArray *)a) ///< Cast to SubArray #define CHAIN(c) ((SubChain *)c) ///< Cast to SubChain #define CLIENT(c) ((SubClient *)c) ///< Cast to SubClient #define GRAB(g) ((SubGrab *)g) ///< Cast to SubGrab #define GRAVITY(g) ((SubGravity *)g) ///< Cast to SubGravity #define HOOK(h) ((SubHook *)h) ///< Cast to SubHook #define ICON(i) ((SubIcon *)i) ///< Cast to SubIcon #define KEYCHAIN(k) ((SubKeychain *)k) ///< Cast to SubKeychain #define PANEL(p) ((SubPanel *)p) ///< Cast to SubPanel #define SCREEN(s) ((SubScreen *)s) ///< Cast to SubScreen #define STYLE(s) ((SubStyle *)s) ///< Cast to SubStyle #define SUBLET(s) ((SubSublet *)s) ///< Cast to SubSublet #define TAG(t) ((SubTag *)t) ///< Cast to SubTag #define TRAY(t) ((SubTray *)t) ///< Cast to SubTray #define VIEW(v) ((SubView *)v) ///< Cast to SubView /* }}} */ /* Flags {{{ */ /* Data types */ #define SUB_TYPE_CLIENT (1L << 0) ///< Client #define SUB_TYPE_GRAB (1L << 1) ///< Grab #define SUB_TYPE_GRAVITY (1L << 2) ///< Gravity #define SUB_TYPE_HOOK (1L << 3) ///< Hook #define SUB_TYPE_PANEL (1L << 4) ///< Panel #define SUB_TYPE_SCREEN (1L << 5) ///< Screen #define SUB_TYPE_STYLE (1L << 6) ///< Style #define SUB_TYPE_TAG (1L << 7) ///< Tag #define SUB_TYPE_TRAY (1L << 8) ///< Tray #define SUB_TYPE_VIEW (1L << 9) ///< View /* Loglevel flags */ #define SUB_LOG_WARN (1L << 0) ///< Log warning messages #define SUB_LOG_ERROR (1L << 1) ///< Log error messages #define SUB_LOG_SUBLET (1L << 2) ///< Log error messages #define SUB_LOG_DEPRECATED (1L << 3) ///< Log deprecation messages #define SUB_LOG_EVENTS (1L << 4) ///< Log event messages #define SUB_LOG_RUBY (1L << 5) ///< Log ruby messages #define SUB_LOG_XERROR (1L << 6) ///< Log X error messages #define SUB_LOG_SUBTLE (1L << 7) ///< Log subtle messages #define SUB_LOG_DEBUG (1L << 8) ///< Log other debug messages /* Call flags */ #define SUB_CALL_HOOKS (1L << 10) ///< Call generic hook #define SUB_CALL_CONFIGURE (1L << 11) ///< Call watch hook #define SUB_CALL_RUN (1L << 12) ///< Call run hook #define SUB_CALL_DATA (1L << 13) ///< Call data hook #define SUB_CALL_WATCH (1L << 14) ///< Call watch hook #define SUB_CALL_DOWN (1L << 15) ///< Call mouse down hook #define SUB_CALL_OVER (1L << 16) ///< Call mouse over hook #define SUB_CALL_OUT (1L << 17) ///< Call mouse out hook #define SUB_CALL_UNLOAD (1L << 18) ///< Call unload hook /* Hook flags */ #define SUB_HOOK_START (1L << 10) ///< Start hook #define SUB_HOOK_RELOAD (1L << 11) ///< Reload hook #define SUB_HOOK_EXIT (1L << 12) ///< Exit hook #define SUB_HOOK_TILE (1L << 13) ///< Tile hook #define SUB_HOOK_TYPE_CLIENT (1L << 14) ///< Client hooks #define SUB_HOOK_TYPE_VIEW (1L << 15) ///< View hooks #define SUB_HOOK_TYPE_TAG (1L << 16) ///< Tag hooks #define SUB_HOOK_ACTION_CREATE (1L << 17) ///< Create action #define SUB_HOOK_ACTION_MODE (1L << 18) ///< Mode action #define SUB_HOOK_ACTION_GRAVITY (1L << 19) ///< Gravity action #define SUB_HOOK_ACTION_FOCUS (1L << 20) ///< Focus action #define SUB_HOOK_ACTION_KILL (1L << 21) ///< Kill action /* Client flags */ #define SUB_CLIENT_DEAD (1L << 10) ///< Dead window #define SUB_CLIENT_FOCUS (1L << 11) ///< Send focus message #define SUB_CLIENT_INPUT (1L << 12) ///< Active/passive focus-model #define SUB_CLIENT_CLOSE (1L << 13) ///< Send close message #define SUB_CLIENT_UNMAP (1L << 14) ///< Ignore unmaps #define SUB_CLIENT_ARRANGE (1L << 15) ///< Re-arrange client #define SUB_CLIENT_MODE_FULL (1L << 16) ///< Fullscreen mode (also used in tags) #define SUB_CLIENT_MODE_FLOAT (1L << 17) ///< Float mode #define SUB_CLIENT_MODE_STICK (1L << 18) ///< Stick mode #define SUB_CLIENT_MODE_STICK_SCREEN (1L << 19) ///< Stick tagged screen mode #define SUB_CLIENT_MODE_URGENT (1L << 20) ///< Urgent mode #define SUB_CLIENT_MODE_RESIZE (1L << 21) ///< Resize mode #define SUB_CLIENT_MODE_ZAPHOD (1L << 22) ///< Zaphod mode #define SUB_CLIENT_MODE_FIXED (1L << 23) ///< Fixed size mode #define SUB_CLIENT_MODE_CENTER (1L << 24) ///< Center position mode #define SUB_CLIENT_MODE_BORDERLESS (1L << 25) ///< Borderless #define SUB_CLIENT_TYPE_NORMAL (1L << 26) ///< Normal type (also used in match) #define SUB_CLIENT_TYPE_DESKTOP (1L << 27) ///< Desktop type #define SUB_CLIENT_TYPE_DOCK (1L << 28) ///< Dock type #define SUB_CLIENT_TYPE_TOOLBAR (1L << 29) ///< Toolbar type #define SUB_CLIENT_TYPE_SPLASH (1L << 30) ///< Splash type #define SUB_CLIENT_TYPE_DIALOG (1L << 31) ///< Dialog type /* Client restack */ #define SUB_CLIENT_RESTACK_DOWN 0 ///< Restack down #define SUB_CLIENT_RESTACK_UP 1 ///< Restack up /* Drag flags */ #define SUB_DRAG_START (1L << 0) ///< Drag start #define SUB_DRAG_MOVE (1L << 1) ///< Drag move #define SUB_DRAG_RESIZE (1L << 2) ///< Drag resize /* Grab flags */ #define SUB_GRAB_KEY (1L << 10) ///< Key grab #define SUB_GRAB_MOUSE (1L << 11) ///< Mouse grab #define SUB_GRAB_SPAWN (1L << 12) ///< Spawn an app #define SUB_GRAB_PROC (1L << 13) ///< Grab with proc #define SUB_GRAB_CHAIN_START (1L << 14) ///< Chain grab start #define SUB_GRAB_CHAIN_LINK (1L << 15) ///< Chain grab link #define SUB_GRAB_CHAIN_END (1L << 16) ///< Chain grab end #define SUB_GRAB_VIEW_FOCUS (1L << 17) ///< Jump to view #define SUB_GRAB_VIEW_SWAP (1L << 18) ///< Jump to view #define SUB_GRAB_VIEW_SELECT (1L << 19) ///< Jump to view #define SUB_GRAB_SCREEN_JUMP (1L << 20) ///< Jump to screen #define SUB_GRAB_SUBTLE_RELOAD (1L << 21) ///< Reload subtle #define SUB_GRAB_SUBTLE_RESTART (1L << 22) ///< Restart subtle #define SUB_GRAB_SUBTLE_QUIT (1L << 23) ///< Quit subtle #define SUB_GRAB_WINDOW_MOVE (1L << 24) ///< Resize window #define SUB_GRAB_WINDOW_RESIZE (1L << 25) ///< Move window #define SUB_GRAB_WINDOW_TOGGLE (1L << 26) ///< Toggle window #define SUB_GRAB_WINDOW_STACK (1L << 27) ///< Stack window #define SUB_GRAB_WINDOW_SELECT (1L << 28) ///< Select window #define SUB_GRAB_WINDOW_GRAVITY (1L << 29) ///< Set gravity of window #define SUB_GRAB_WINDOW_KILL (1L << 30) ///< Kill window /* Grab dirctions flags */ #define SUB_GRAB_DIRECTION_UP (1L << 0) ///< Direction up #define SUB_GRAB_DIRECTION_RIGHT (1L << 1) ///< Direction right #define SUB_GRAB_DIRECTION_DOWN (1L << 2) ///< Direction down #define SUB_GRAB_DIRECTION_LEFT (1L << 3) ///< Direction left /* Gravity flags */ #define SUB_GRAVITY_HORZ (1L << 10) ///< Gravity tile gravity horizontally #define SUB_GRAVITY_VERT (1L << 11) ///< Gravity tile gravity vertically /* Panel flags */ #define SUB_PANEL_SUBLET (1L << 10) ///< Panel sublet type #define SUB_PANEL_COPY (1L << 11) ///< Panel copy type #define SUB_PANEL_VIEWS (1L << 12) ///< Panel views type #define SUB_PANEL_TITLE (1L << 13) ///< Panel title type #define SUB_PANEL_KEYCHAIN (1L << 14) ///< Panel keychain type #define SUB_PANEL_TRAY (1L << 15) ///< Panel tray type #define SUB_PANEL_ICON (1L << 16) ///< Panel icon type #define SUB_PANEL_SPACER1 (1L << 17) ///< Panel spacer1 #define SUB_PANEL_SPACER2 (1L << 18) ///< Panel spacer2 #define SUB_PANEL_SEPARATOR1 (1L << 19) ///< Panel separator1 #define SUB_PANEL_SEPARATOR2 (1L << 20) ///< Panel separator2 #define SUB_PANEL_BOTTOM (1L << 21) ///< Panel bottom #define SUB_PANEL_HIDDEN (1L << 22) ///< Panel hidden #define SUB_PANEL_CENTER (1L << 23) ///< Panel center #define SUB_PANEL_SUBLETS (1L << 24) ///< Panel sublets #define SUB_PANEL_DOWN (1L << 25) ///< Panel mouse down #define SUB_PANEL_OVER (1L << 26) ///< Panel mouse over #define SUB_PANEL_OUT (1L << 27) ///< Panel mouse out /* Sublet flags */ #define SUB_SUBLET_INTERVAL (1L << 10) ///< Sublet has interval #define SUB_SUBLET_INOTIFY (1L << 11) ///< Sublet with inotify #define SUB_SUBLET_SOCKET (1L << 12) ///< Sublet with socket #define SUB_SUBLET_RUN (1L << 13) ///< Sublet run function #define SUB_SUBLET_DATA (1L << 14) ///< Sublet data function #define SUB_SUBLET_WATCH (1L << 15) ///< Sublet watch function #define SUB_SUBLET_UNLOAD (1L << 16) ///< Sublet unload function /* Screen flags */ #define SUB_SCREEN_PANEL1 (1L << 10) ///< Screen sanel1 enabled #define SUB_SCREEN_PANEL2 (1L << 11) ///< Screen sanel2 enabled #define SUB_SCREEN_STIPPLE (1L << 12) ///< Screen stipple enabled /* Style flags */ #define SUB_STYLE_FONT (1L << 10) ///< Style has custom font #define SUB_STYLE_SEPARATOR (1L << 11) ///< Style has separator /* Subtle flags */ #define SUB_SUBTLE_DEBUG (1L << 0) ///< Debug enabled #define SUB_SUBTLE_CHECK (1L << 1) ///< Check config #define SUB_SUBTLE_RUN (1L << 2) ///< Run event loop #define SUB_SUBTLE_URGENT (1L << 3) ///< Urgent transients #define SUB_SUBTLE_RESIZE (1L << 4) ///< Respect size #define SUB_SUBTLE_XINERAMA (1L << 5) ///< Using Xinerama #define SUB_SUBTLE_XRANDR (1L << 6) ///< Using Xrandr #define SUB_SUBTLE_EWMH (1L << 7) ///< EWMH set #define SUB_SUBTLE_REPLACE (1L << 8) ///< Replace previous wm #define SUB_SUBTLE_RESTART (1L << 9) ///< Restart #define SUB_SUBTLE_RELOAD (1L << 10) ///< Reload config #define SUB_SUBTLE_TRAY (1L << 11) ///< Use tray #define SUB_SUBTLE_TILING (1L << 12) ///< Enable tiling #define SUB_SUBTLE_FOCUS_CLICK (1L << 13) ///< Click to focus #define SUB_SUBTLE_SKIP_WARP (1L << 14) ///< Skip pointer warp #define SUB_SUBTLE_SKIP_URGENT_WARP (1L << 15) ///< Skip urgent warp /* Tag flags */ #define SUB_TAG_GRAVITY (1L << 10) ///< Gravity property #define SUB_TAG_GEOMETRY (1L << 11) ///< Geometry property #define SUB_TAG_POSITION (1L << 12) ///< Position property #define SUB_TAG_PROC (1L << 13) ///< Tagging proc (must be <16) /* Tag matcher */ #define SUB_TAG_MATCH_NAME (1L << 10) ///< Match WM_NAME #define SUB_TAG_MATCH_INSTANCE (1L << 11) ///< Match instance of WM_CLASS #define SUB_TAG_MATCH_CLASS (1L << 12) ///< Match class of WM_CLASS #define SUB_TAG_MATCH_ROLE (1L << 13) ///< Match role of window #define SUB_TAG_MATCH_TYPE (1L << 14) ///< Match type of window #define SUB_TAG_MATCH_AND (1L << 15) ///< Match logical AND (must be <26) /* Tray flags */ #define SUB_TRAY_DEAD (1L << 10) ///< Dead window #define SUB_TRAY_CLOSE (1L << 12) ///< Send close message #define SUB_TRAY_UNMAP (1L << 11) ///< Ignore unmaps /* Text flags */ #define SUB_TEXT_EMPTY (1L << 0) ///< Empty text #define SUB_TEXT_BITMAP (1L << 1) ///< Text bitmap #define SUB_TEXT_PIXMAP (1L << 2) ///< Text pixmap /* View flags */ #define SUB_VIEW_ICON (1L << 10) ///< View icon #define SUB_VIEW_ICON_ONLY (1L << 11) ///< Icon only #define SUB_VIEW_DYNAMIC (1L << 12) ///< Dynamic views /* Special flags */ #define SUB_RUBY_DATA (1L << 30) ///< Object stores ruby data /* Shortcuts */ #define TYPES_ALL \ (SUB_CLIENT_TYPE_DESKTOP|SUB_CLIENT_TYPE_DOCK| \ SUB_CLIENT_TYPE_SPLASH|SUB_CLIENT_TYPE_DIALOG) ///< All type flags #define MODES_ALL \ (SUB_CLIENT_MODE_FULL|SUB_CLIENT_MODE_FLOAT| \ SUB_CLIENT_MODE_STICK|SUB_CLIENT_MODE_URGENT| \ SUB_CLIENT_MODE_RESIZE|SUB_CLIENT_MODE_ZAPHOD| \ SUB_CLIENT_MODE_FIXED|SUB_CLIENT_MODE_BORDERLESS) ///< All mode flags /* State action */ #define _NET_WM_STATE_REMOVE 0L /// Remove/unset property #define _NET_WM_STATE_ADD 1L /// Add/set property #define _NET_WM_STATE_TOGGLE 2L /// Toggle property /* XEmbed messages */ #define XEMBED_EMBEDDED_NOTIFY 0L ///< Start embedding #define XEMBED_WINDOW_ACTIVATE 1L ///< Tray has focus #define XEMBED_WINDOW_DEACTIVATE 2L ///< Tray has no focus #define XEMBED_REQUEST_FOCUS 3L #define XEMBED_FOCUS_IN 4L ///< Focus model #define XEMBED_FOCUS_OUT 5L #define XEMBED_FOCUS_NEXT 6L #define XEMBED_FOCUS_PREV 7L #define XEMBED_GRAB_KEY 8L #define XEMBED_UNGRAB_KEY 9L #define XEMBED_MODALITY_ON 10L #define XEMBED_MODALITY_OFF 11L #define XEMBED_REGISTER_ACCELERATOR 12L #define XEMBED_UNREGISTER_ACCELERATOR 13L #define XEMBED_ACTIVATE_ACCELERATOR 14L /* Details for XEMBED_FOCUS_IN */ #define XEMBED_FOCUS_CURRENT 0L /// Focus default #define XEMBED_FOCUS_FIRST 1L #define XEMBED_FOCUS_LAST 2L /* Flags for _XEMBED_INFO */ #define XEMBED_MAPPED (1L << 0) ///< Tray mapped /* Flags for MWM */ #define MWM_FLAG_FUNCTIONS (1L << 0) ///< Use functions #define MWM_FLAG_DECORATIONS (1L << 1) ///< Use decorations /* Flags for MWM decoration */ #define MWM_DECOR_ALL (1L << 0) ///< All decorations #define MWM_DECOR_BORDER (1L << 1) ///< Window borders #define MWM_DECOR_RESIZEH (1L << 2) ///< Resize handles #define MWM_DECOR_TITLE (1L << 3) ///< Window title #define MWM_DECOR_MENU (1L << 4) ///< Window menu #define MWM_DECOR_MINIMIZE (1L << 5) ///< Minimize button #define MWM_DECOR_MAXIMIZE (1L << 6) ///< Maximize button /* }}} */ /* Typedefs {{{ */ typedef struct subarray_t /* {{{ */ { int ndata; ///< Array data count void **data; ///< Array data } SubArray; /* }}} */ typedef struct subkeychain_t /* {{{ */ { int len; ///< Keychain length char *keys; ///< Keychain keys } SubKeychain; /* }}} */ typedef struct subclient_t /* {{{ */ { FLAGS flags; ///< Client flags char *name, *instance, *klass, *role; ///< Client instance, klass TAGS tags; ///< Client tags Window win, leader; ///< Client window and leader Colormap cmap; ///< Client colormap XRectangle geom; ///< Client geom float minr, maxr; ///< Client ratios int minw, minh, maxw, maxh, incw, inch, basew, baseh; ///< Client sizes int dir, screenid, gravityid; ///< Client restacking dir, current screen id, current gravity id int *gravities; ///< Client gravities for views } SubClient; /* }}} */ typedef enum subewmh_t /* {{{ */ { /* ICCCM */ SUB_EWMH_WM_NAME, ///< Name of window SUB_EWMH_WM_CLASS, ///< Class of window SUB_EWMH_WM_STATE, ///< Window state SUB_EWMH_WM_PROTOCOLS, ///< Supported protocols SUB_EWMH_WM_TAKE_FOCUS, ///< Send focus messages SUB_EWMH_WM_DELETE_WINDOW, ///< Send close messages SUB_EWMH_WM_NORMAL_HINTS, ///< Window normal hints SUB_EWMH_WM_SIZE_HINTS, ///< Window size hints SUB_EWMH_WM_HINTS, ///< Window hints SUB_EWMH_WM_WINDOW_ROLE, ///< Window role SUB_EWMH_WM_CLIENT_LEADER, ///< Client leader /* EWMH */ SUB_EWMH_NET_SUPPORTED, ///< Supported states SUB_EWMH_NET_CLIENT_LIST, ///< List of clients SUB_EWMH_NET_CLIENT_LIST_STACKING, ///< List of clients SUB_EWMH_NET_NUMBER_OF_DESKTOPS, ///< Total number of views SUB_EWMH_NET_DESKTOP_NAMES, ///< Names of the views SUB_EWMH_NET_DESKTOP_GEOMETRY, ///< Desktop geometry SUB_EWMH_NET_DESKTOP_VIEWPORT, ///< Viewport of the view SUB_EWMH_NET_CURRENT_DESKTOP, ///< Number of current view SUB_EWMH_NET_ACTIVE_WINDOW, ///< Focus window SUB_EWMH_NET_WORKAREA, ///< Workarea of the views SUB_EWMH_NET_SUPPORTING_WM_CHECK, ///< Check for compliant window manager SUB_EWMH_NET_WM_FULL_PLACEMENT, ///< WM does all placement SUB_EWMH_NET_FRAME_EXTENTS, ///< Extents of the client frame /* Client */ SUB_EWMH_NET_CLOSE_WINDOW, ///< Close window SUB_EWMH_NET_RESTACK_WINDOW, ///< Change window stacking SUB_EWMH_NET_MOVERESIZE_WINDOW, ///< Resize window SUB_EWMH_NET_WM_NAME, ///< Name of client SUB_EWMH_NET_WM_PID, ///< PID of client SUB_EWMH_NET_WM_DESKTOP, ///< Desktop client is on SUB_EWMH_NET_WM_STRUT, ///< Strut /* Types */ SUB_EWMH_NET_WM_WINDOW_TYPE, ///< Window type SUB_EWMH_NET_WM_WINDOW_TYPE_DOCK, ///< Dock window SUB_EWMH_NET_WM_WINDOW_TYPE_DESKTOP, ///< Desktop window SUB_EWMH_NET_WM_WINDOW_TYPE_TOOLBAR, ///< Toolbar window SUB_EWMH_NET_WM_WINDOW_TYPE_SPLASH, ///< Splash window SUB_EWMH_NET_WM_WINDOW_TYPE_DIALOG, ///< Dialog window /* States */ SUB_EWMH_NET_WM_STATE, ///< Window state SUB_EWMH_NET_WM_STATE_FULLSCREEN, ///< Fullscreen window SUB_EWMH_NET_WM_STATE_ABOVE, ///< Floating window SUB_EWMH_NET_WM_STATE_STICKY, ///< Sticky window SUB_EWMH_NET_WM_STATE_ATTENTION, ///< Urgent window /* Tray */ SUB_EWMH_NET_SYSTEM_TRAY_OPCODE, ///< Tray messages SUB_EWMH_NET_SYSTEM_TRAY_MESSAGE_DATA, ///< Tray message data SUB_EWMH_NET_SYSTEM_TRAY_SELECTION, ///< Tray selection /* Misc */ SUB_EWMH_UTF8, ///< String encoding SUB_EWMH_MANAGER, ///< Selection manager SUB_EWMH_MOTIF_WM_HINTS, ///< Motif decoration hints /* XEmbed */ SUB_EWMH_XEMBED, ///< XEmbed SUB_EWMH_XEMBED_INFO, ///< XEmbed info /* subtle */ SUB_EWMH_SUBTLE_CLIENT_TAGS, ///< Subtle client tags SUB_EWMH_SUBTLE_CLIENT_RETAG, ///< Subtle client retag SUB_EWMH_SUBTLE_CLIENT_GRAVITY, ///< Subtle client gravity SUB_EWMH_SUBTLE_CLIENT_SCREEN, ///< Subtle client screen SUB_EWMH_SUBTLE_CLIENT_FLAGS, ///< Subtle client flags SUB_EWMH_SUBTLE_GRAVITY_NEW, ///< Subtle gravity new SUB_EWMH_SUBTLE_GRAVITY_FLAGS, ///< Subtle gravity flags SUB_EWMH_SUBTLE_GRAVITY_LIST, ///< Subtle gravity list SUB_EWMH_SUBTLE_GRAVITY_KILL, ///< Subtle gravtiy kill SUB_EWMH_SUBTLE_TAG_NEW, ///< Subtle tag new SUB_EWMH_SUBTLE_TAG_LIST, ///< Subtle tag list SUB_EWMH_SUBTLE_TAG_KILL, ///< Subtle tag kill SUB_EWMH_SUBTLE_TRAY_LIST, ///< Subtle tray list SUB_EWMH_SUBTLE_VIEW_NEW, ///< Subtle view new SUB_EWMH_SUBTLE_VIEW_TAGS, ///< Subtle view tags SUB_EWMH_SUBTLE_VIEW_STYLE, ///< Subtle view style SUB_EWMH_SUBTLE_VIEW_ICONS, ///< Subtle view icons SUB_EWMH_SUBTLE_VIEW_KILL, ///< Subtle view kill SUB_EWMH_SUBTLE_SUBLET_UPDATE, ///< Subtle sublet update SUB_EWMH_SUBTLE_SUBLET_DATA, ///< Subtle sublet data SUB_EWMH_SUBTLE_SUBLET_STYLE, ///< Subtle sublet style SUB_EWMH_SUBTLE_SUBLET_FLAGS, ///< Subtle sublet flags SUB_EWMH_SUBTLE_SUBLET_LIST, ///< Subtle sublet list SUB_EWMH_SUBTLE_SUBLET_KILL, ///< Subtle sublet kill SUB_EWMH_SUBTLE_SCREEN_PANELS, ///< Subtle screen panels SUB_EWMH_SUBTLE_SCREEN_VIEWS, ///< Subtle screen views SUB_EWMH_SUBTLE_SCREEN_JUMP, ///< Subtle screen jump SUB_EWMH_SUBTLE_VISIBLE_TAGS, ///< Subtle visible tags SUB_EWMH_SUBTLE_VISIBLE_VIEWS, ///< Subtle visible views SUB_EWMH_SUBTLE_RENDER, ///< Subtle render SUB_EWMH_SUBTLE_RELOAD, ///< Subtle reload SUB_EWMH_SUBTLE_RESTART, ///< Subtle restart SUB_EWMH_SUBTLE_QUIT, ///< Subtle quit SUB_EWMH_SUBTLE_COLORS, ///< Subtle colors SUB_EWMH_SUBTLE_FONT, ///< Subtle font SUB_EWMH_SUBTLE_DATA, ///< Subtle data SUB_EWMH_SUBTLE_VERSION, ///< Subtle version SUB_EWMH_TOTAL } SubEwmh; /* }}} */ typedef struct subgrab_t /* {{{ */ { FLAGS flags; ///< Grab flags unsigned int code, state; ///< Grab code, stater union subdata_t data; ///< Grab data struct subarray_t *keys; ///< Grab chain keys } SubGrab; /* }}} */ typedef struct subgravity_t /* {{{ */ { FLAGS flags; ///< Gravity flags int quark; ///< Gravity quark XRectangle geom; ///< Gravity geometry } SubGravity; /* }}} */ typedef struct subhook_t /* {{{ */ { FLAGS flags; ///< Hook flags unsigned long proc; ///< Hook proc } SubHook; /* }}} */ typedef struct subicon_t /* {{{ */ { int width, height, bitmap; ///< Icon height, bitmap Pixmap pixmap; ///< Icon pixmap } SubIcon; /* }}} */ typedef struct subpanel_t /* {{{ */ { FLAGS flags; ///< Panel flags int x, width; ///< Panel x, width struct subscreen_t *screen; ///< Panel screen union { struct subkeychain_t *keychain; ///< Panel chain struct subsublet_t *sublet; ///< Panel sublet struct subicon_t *icon; ///< Panel icon }; } SubPanel; /* }}} */ typedef struct subscreen_t /* {{{ */ { FLAGS flags; ///< Screen flags int viewid; ///< Screen current view id XRectangle geom, base; ///< Screen geom, base Pixmap stipple; ///< Screen stipple Drawable drawable; ///< Screen drawable Window panel1, panel2; ///< Screen windows struct subarray_t *panels; ///< Screen panels /* FIXME: Cache ruby object during config */ unsigned long top, bottom; ///< Screen panel values } SubScreen; /* }}} */ typedef struct subseparator_t /* {{{ */ { char *string; ///< Separator string int width; ///< Separator width } SubSeparator; /* }}} */ typedef struct subsublet_t { /* {{{ */ FLAGS flags; ///< Sublet flags int watch, width, styleid; ///< Sublet watch id, width and style id char *name; ///< Sublet name unsigned long instance; ///< Sublet ruby instance, fg, bg and icon color time_t time, interval; ///< Sublet update/interval time struct subtext_t *text; ///< Sublet text } SubSublet; /* }}} */ typedef struct subsides_t /* {{{ */ { int top, right, bottom, left; ///< Side values } SubSides; /* }}} */ typedef struct substyle_t /* {{{ */ { FLAGS flags; ///< Style flags char *name; ///< Style name int min; ///< Style min width long fg, bg, icon, top, right, bottom, left; ///< Style colors struct subsides_t border, padding, margin; ///< Style border, padding and margin struct subarray_t *styles; ///< Style state list struct subfont_t *font; ///< Style font struct subseparator_t *separator; ///< Style separator } SubStyle; /* }}} */ typedef struct subsubtle_t /* {{{ */ { FLAGS flags; ///< Subtle flags int loglevel, width, height; ///< Subtle loglevel and screen size int ph, step, snap; ///< Subtle properties int visible_tags, visible_views; ///< Subtle visible tags and views int client_tags, urgent_tags; ///< Subtle clients and urgent tags unsigned long gravity; ///< Subtle default gravity Display *dpy; ///< Subtle Xorg display struct subgrab_t *keychain; ///< Subtle current keychain struct subarray_t *clients; ///< Subtle clients struct subarray_t *grabs; ///< Subtle grabs struct subarray_t *gravities; ///< Subtle gravities struct subarray_t *hooks; ///< Subtle hooks struct subarray_t *screens; ///< Subtle screens struct subarray_t *sublets; ///< Subtle sublets struct subarray_t *tags; ///< Subtle tags struct subarray_t *trays; ///< Subtle trays struct subarray_t *views; ///< Subtle views #ifdef HAVE_SYS_INOTIFY_H int notify; ///< Subtle inotify descriptor #endif /* HAVE_SYS_INOTIFY_H */ struct { char *config, *sublets; ///< Subtle paths } paths; struct { Window support, focus[HISTORYSIZE], tray; } windows; ///< Subtle windows struct { struct subpanel_t tray, keychain; } panels; ///< Subtle panels struct { struct substyle_t all, views, title, sublets, separator, clients, subtle; ///< Subtle base styles struct substyle_t *urgent, *occupied, *focus, *visible, *viewsep, *subletsep; ///< For faster access to sub-styles } styles; ///< Subtle styles struct { GC stipple, invert, draw; } gcs; ///< Subtle graphic contexts struct { Cursor arrow, move, resize; } cursors; ///< Subtle cursors } SubSubtle; /* }}} */ typedef struct subtag_t /* {{{ */ { FLAGS flags; ///< Tag flags char *name; ///< Tag name unsigned long gravityid, proc; ///< Tag gravity, proc int screenid; ///< Tag screen XRectangle geom; ///< Tag geometry struct subarray_t *matcher; ///< Tag matcher } SubTag; /* }}} */ typedef struct subtextitem_t /* {{{ */ { int flags, width, height; ///< Text flags, width, height long color; ///< Text color union subdata_t data; ///< Text data } SubTextItem; /* }}} */ typedef struct subtext_t /* {{{ */ { struct subtextitem_t **items; ///< Item text items int flags, nitems, width; ///< Item flags, count, width } SubText; /* }}} */ typedef struct subtray_t /* {{{ */ { FLAGS flags; ///< Tray flags char *name; ///< Tray name Window win; ///< Tray window int width; ///< Tray width } SubTray; /* }}} */ typedef struct subview_t /* {{{ */ { FLAGS flags; ///< View flags char *name; ///< View name TAGS tags; ///< View tags Window focus; ///< View window, focus int width, styleid; ///< View width, style id struct subicon_t *icon; ///< View icon } SubView; /* }}} */ extern SubSubtle *subtle; /* }}} */ /* array.c {{{ */ SubArray *subArrayNew(void); ///< Create array void subArrayPush(SubArray *a, void *elem); ///< Push element to array void subArrayInsert(SubArray *a, int pos, void *elem); ///< Insert element at pos void subArrayRemove(SubArray *a, void *elem); ///< Remove element from array void *subArrayGet(SubArray *a, int idx); ///< Get element int subArrayIndex(SubArray *a, void *elem); ///< Find array id of element void subArraySort(SubArray *a, ///< Sort array with given compare function int(*compar)(const void *a, const void *b)); void subArrayClear(SubArray *a, int clean); ///< Delete all elements void subArrayKill(SubArray *a, int clean); ///< Kill array with all elements /* }}} */ /* client.c {{{ */ SubClient *subClientNew(Window win); ///< Create client void subClientConfigure(SubClient *c); ///< Send configure request void subClientDimension(int id); ///< Dimension clients void subClientFocus(SubClient *c, int warp); ///< Focus client SubClient *subClientNext(int screenid, int jump); ///< Focus next client void subClientWarp(SubClient *c); ///< Warp pointer to client void subClientDrag(SubClient *c, int mode, int direction); ///< Move/drag client void subClientUpdate(int vid); ///< Update clients void subClientTag(SubClient *c, int tag, int *flags); ///< Tag client void subClientRetag(SubClient *c, int *flags); ///< Update client tags void subClientResize(SubClient *c, XRectangle *bounds, int size_hints); ///< Resize client for screen void subClientRestack(SubClient *c, int dir); ///< Restack clients void subClientArrange(SubClient *c, int gravityid, int screenid); ///< Arrange client void subClientToggle(SubClient *c, int flags, int set_gravity); ///< Toggle client flags void subClientSetStrut(SubClient *c); ///< Set client strut void subClientSetProtocols(SubClient *c); ///< Set client protocols void subClientSetSizeHints(SubClient *c, int *flags); ///< Set client normal hints void subClientSetWMHints(SubClient *c, int *flags); ///< Set client WM hints void subClientSetMWMHints(SubClient *c); ///< Set client MWM hints void subClientSetState(SubClient *c, int *flags); ///< Set client WM state void subClientSetTransient(SubClient *c, int *flags); ///< Set client transient void subClientSetType(SubClient *c, int *flags); ///< Set client type void subClientClose(SubClient *c); ///< Close client void subClientKill(SubClient *c); ///< Kill client void subClientPublish(int restack); ///< Publish all clients /* }}} */ /* display.c {{{ */ void subDisplayInit(const char *display); ///< Create display void subDisplayConfigure(void); ///< Configure display void subDisplayScan(void); ///< Scan root window void subDisplayPublish(void); ///< Publish colors void subDisplayFinish(void); ///< Kill display /* }}} */ /* event.c {{{ */ void subEventWatchAdd(int fd); ///< Add watch fd void subEventWatchDel(int fd); ///< Del watch fd void subEventLoop(void); ///< Event loop void subEventFinish(void); ///< Finish events /* }}} */ /* ewmh.c {{{ */ void subEwmhInit(void); ///< Init atoms/hints Atom subEwmhGet(SubEwmh e); ///< Get atom SubEwmh subEwmhFind(Atom atom); ///< Find atom id long subEwmhGetWMState(Window win); ///< Get window WM state long subEwmhGetXEmbedState(Window win); ///< Get window XEmbed state void subEwmhSetWindows(Window win, SubEwmh e, Window *values, int size); ///< Set window properties void subEwmhSetCardinals(Window win, SubEwmh e, long *values, int size); ///< Set cardinal properties void subEwmhSetString(Window win, SubEwmh e, char *value); ///< Set string property void subEwmhSetWMState(Window win, long state); ///< Set window WM state void subEwmhTranslateWMState(Atom atom, int *flags); ///< Translate WM states void subEwmhTranslateClientMode(int client_flags, int *flags); ///< Translate client modes int subEwmhMessage(Window win, SubEwmh e, long mask, long data0, long data1, long data2, long data3, long data4); ///< Send message void subEwmhFinish(void); ///< Unset EWMH properties /* }}} */ /* grab.c {{{ */ void subGrabInit(void); ///< Init keymap SubGrab *subGrabNew(const char *keys, int *duplicate); ///< Create grab SubGrab *subGrabFind(int code, unsigned int mod); ///< Find grab void subGrabSet(Window win, int mask); ///< Grab window void subGrabUnset(Window win); ///< Ungrab window int subGrabCompare(const void *a, const void *b); ///< Compare grabs void subGrabKill(SubGrab *g); ///< Kill grab /* }}} */ /* gravity.c {{{ */ SubGravity *subGravityNew(const char *name, XRectangle *geom); ///< Create gravity void subGravityGeometry(SubGravity *g, XRectangle *bounds, XRectangle *geom); ///< Calculate gravity geometry void subGravityKill(SubGravity *g); ///< Kill gravity int subGravityFind(const char *name, int quark); ///< Find gravity id void subGravityPublish(void); ///< Publish gravities /* }}} */ /* hook.c {{{ */ SubHook *subHookNew(int type, unsigned long proc); ///< Create hook void subHookCall(int type, void *data); ///< Call hook void subHookKill(SubHook *h); ///< Kill hook /* }}} */ /* panel.c {{{ */ SubPanel *subPanelNew(int type); ///< Create new panel void subPanelUpdate(SubPanel *p); ///< Update panels void subPanelRender(SubPanel *p, Drawable drawable); ///< Render panels int subPanelCompare(const void *a, const void *b); ///< Compare two panels void subPanelAction(SubArray *panels, int type, int x, int y, int button, int bottom); ///< Handle panel action void subPanelGeometry(SubPanel *p, SubStyle *s, XRectangle *geom); ///< Get panel geometry void subPanelPublish(void); ///< Publish sublets void subPanelKill(SubPanel *p); ///< Kill panel /* }}} */ /* ruby.c {{{ */ void subRubyInit(void); ///< Init Ruby stack void subRubyLoadConfig(void); ///< Load config file void subRubyReloadConfig(void); ///< Reload config file void subRubyLoadSublet(const char *file); ///< Load sublet void subRubyUnloadSublet(SubPanel *p); ///< Unload sublet void subRubyLoadSublets(void); ///< Load sublets void subRubyLoadPanels(void); ///< Load panels int subRubyCall(int type, unsigned long proc, void *data); ///< Call Ruby script int subRubyRelease(unsigned long recv); ///< Release receiver void subRubyFinish(void); ///< Kill Ruby stack /* }}} */ /* screen.c {{{ */ void subScreenInit(void); ///< Init screens SubScreen *subScreenNew(int x, int y, unsigned int width, unsigned int height); ///< Create screen SubScreen *subScreenFind(int x, int y, int *sid); ///< Find screen by coordinates SubScreen * subScreenCurrent(int *sid); ///< Get current screen void subScreenConfigure(void); ///< Configure screens void subScreenUpdate(void); ///< Update screens void subScreenRender(void); ///< Render screens void subScreenResize(void); ///< Update screen sizes void subScreenWarp(SubScreen *s); ///< Warp pointer to screen void subScreenPublish(void); ///< Publish screens void subScreenKill(SubScreen *s); ///< Kill screen /* }}} */ /* style.c {{{ */ SubStyle *subStyleNew(void); ///< Create new style SubStyle *subStyleFind(SubStyle *s, char *name, int *idx); ///< Find state void subStyleReset(SubStyle *s, int val); ///< Reset style values to given val void subStyleMerge(SubStyle *s1, SubStyle *s2); ///< Merge style values void subStyleKill(SubStyle *s); ///< Kill style void subStyleUpdate(void); ///< Update values /* }}} */ /* subtle.c {{{ */ XPointer * subSubtleFind(Window win, XContext id); ///< Find window time_t subSubtleTime(void); ///< Get current time void subSubtleLog(int level, const char *file, int line, const char *format, ...); ///< Print messages void subSubtleFinish(void); ///< Finish subtle /* }}} */ /* tag.c {{{ */ SubTag *subTagNew(char *name, int *duplicate); ///< Create tag void subTagMatcherAdd(SubTag *t, int type, char *pattern, int and); ///< Add a matcher int subTagMatcherCheck(SubTag *t, SubClient *c); ///< Check for match void subTagPublish(void); ///< Publish tags void subTagKill(SubTag *t); ///< Delete tag /* }}} */ /* text.c {{{ */ SubText *subTextNew(void); ///< Create text int subTextParse(SubText *t, SubFont *f, char *text); ///< Parse string void subTextRender(SubText *t, SubFont *f, GC gc, Window win, int x, int y, long fg, long icon, long bg); ///< Render text void subTextKill(SubText *t); ///< Delete text /* }}} */ /* tray.c {{{ */ SubTray *subTrayNew(Window win); ///< Create tray void subTrayConfigure(SubTray *t); ///< Configure tray void subTrayUpdate(void); ///< Update tray bar void subTraySetState(SubTray *t); ///< Set state void subTraySelect(void); ///< Set selection void subTrayDeselect(void); ///< Get selection void subTrayClose(SubTray *t); ///< Close tray void subTrayKill(SubTray *t); ///< Delete tray void subTrayPublish(void); ///< Publish trays /* }}} */ /* view.c {{{ */ SubView *subViewNew(char *name, char *tags); ///< Create view void subViewFocus(SubView *v, int screenid, int swap, int focus); ///< Focus view void subViewKill(SubView *v); ///< Kill view void subViewPublish(void); ///< Publish views /* }}} */ #endif /* SUBTLE_H */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/tag.c0000644000175000017500000001342711770332063017225 0ustar formorerformorer /** * @package subtle * * @file Tag functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/tag.c,v 3209 2012/05/22 23:44:10 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" #define MATCHER(m) ((TagMatcher *)m) /* Typedef {{{ */ typedef struct tagmatcher_t { FLAGS flags; struct tagmatcher_t *and; regex_t *regex; } TagMatcher; /* }}} */ /* Private */ /* TagClear {{{ */ static void TagClear(SubTag *t) { int i; assert(t); /* Clear matcher */ for(i = 0; t->matcher && i < t->matcher->ndata; i++) { TagMatcher *m = (TagMatcher *)t->matcher->data[i]; if(m->regex) subSharedRegexKill(m->regex); free(m); } subArrayClear(t->matcher, False); } /* }}} */ /* TagFind {{{ */ static SubTag * TagFind(char *name) { int i; SubTag *t = NULL; assert(name); /* Linear search */ for(i = 0; i < subtle->tags->ndata; i++) { t = TAG(subtle->tags->data[i]); if(0 == strcmp(t->name, name)) return t; } return NULL; } /* }}} */ /* TagMatch {{{ */ static int TagMatch(TagMatcher *m, SubClient *c) { /* Complex matching */ if((m->regex && /* Check WM_NAME */ ((m->flags & SUB_TAG_MATCH_NAME && c->name && subSharedRegexMatch(m->regex, c->name)) || /* Check instance part of WM_CLASS */ (m->flags & SUB_TAG_MATCH_INSTANCE && c->instance && subSharedRegexMatch(m->regex, c->instance)) || /* Check class part of WM_CLASS */ (m->flags & SUB_TAG_MATCH_CLASS && c->klass && subSharedRegexMatch(m->regex, c->klass)) || /* Check WM_ROLE */ (m->flags & SUB_TAG_MATCH_ROLE && c->role && subSharedRegexMatch(m->regex, c->role)))) || /* Check _NET_WM_WINDOW_TYPE */ (m->flags & SUB_TAG_MATCH_TYPE && c->flags & (m->flags & (SUB_CLIENT_TYPE_NORMAL|TYPES_ALL)))) return True; return False; } /* }}} */ /* Public */ /** subTagNew {{{ * @brief Create new tag * @param[in] name Name of the tag * @param[out] duplicate Added twice * @return Returns a #SubTag or \p NULL **/ SubTag * subTagNew(char *name, int *duplicate) { SubTag *t = NULL; assert(name); /* Check if tag already exists */ if(duplicate && (t = TagFind(name))) { *duplicate = True; } else { /* Create new tag */ t = TAG(subSharedMemoryAlloc(1, sizeof(SubTag))); t->flags = SUB_TYPE_TAG; t->name = strdup(name); if(duplicate) *duplicate = False; } subSubtleLogDebugSubtle("New: name=%s\n", name); return t; } /* }}} */ /** subTagMatcherAdd {{{ * @brief Add a matcher to a tag * @param[in] t A #SubTag * @param[in] type Matcher type * @param[in] pattern Regex * @param[in] and Logical AND with last matcher **/ void subTagMatcherAdd(SubTag *t, int type, char *pattern, int and) { TagMatcher *m = NULL; regex_t *regex = NULL; assert(t); /* Prevent emtpy regex */ if(pattern && 0 != strlen(pattern)) regex = subSharedRegexNew(pattern); /* Remove matcher types that need a regexp */ if(!regex) type &= ~(SUB_TAG_MATCH_NAME|SUB_TAG_MATCH_INSTANCE| SUB_TAG_MATCH_CLASS|SUB_TAG_MATCH_ROLE); /* Check if anything is left for matching */ if(0 < type) { /* Create new matcher */ m = MATCHER(subSharedMemoryAlloc(1, sizeof(TagMatcher))); m->flags = type; m->regex = regex; /* Create on demand to safe memory */ if(NULL == t->matcher) t->matcher = subArrayNew(); else if(and && 0 < t->matcher->ndata) { /* Link matcher */ MATCHER(t->matcher->data[t->matcher->ndata - 1])->and = m; m->flags |= SUB_TAG_MATCH_AND; } subArrayPush(t->matcher, (void *)m); } } /* }}} */ /** subTagMatcherCheck {{{ * @brief Check if client matches tag * @param[in] t A #SubTag * @param[in] c A #SubClient * @retval True Client matches * @retval False Client doesn't match **/ int subTagMatcherCheck(SubTag *t, SubClient *c) { int i; assert(t && c); /* Check if a matcher and client fit together */ for(i = 0; t->matcher && i < t->matcher->ndata; i++) { TagMatcher *m = MATCHER(t->matcher->data[i]); /* Exclude AND linked matcher */ if(!(m->flags & SUB_TAG_MATCH_AND)) { int and = True; TagMatcher *cur = m; /* Check current matcher and chain */ while(and && cur) { and = TagMatch(cur, c); cur = cur->and; } if(and) return True; } } return False; } /* }}} */ /** subTagKill {{{ * @brief Delete tag * @param[in] t A #SubTag **/ void subTagKill(SubTag *t) { assert(t); /* Hook: Kill */ subHookCall((SUB_HOOK_TYPE_TAG|SUB_HOOK_ACTION_KILL), (void *)t); /* Remove matcher */ if(t->matcher) { TagClear(t); subArrayKill(t->matcher, False); } /* Remove proc */ if(t->flags & SUB_TAG_PROC) subRubyRelease(t->proc); free(t->name); free(t); subSubtleLogDebugSubtle("Kill\n"); } /* }}} */ /* All */ /** subTagPublish {{{ * @brief Publish tags **/ void subTagPublish(void) { int i; char **names = NULL; assert(0 < subtle->tags->ndata); names = (char **)subSharedMemoryAlloc(subtle->tags->ndata, sizeof(char *)); for(i = 0; i < subtle->tags->ndata; i++) names[i] = TAG(subtle->tags->data[i])->name; /* EWMH: Tag list */ subSharedPropertySetStrings(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_TAG_LIST), names, i); XSync(subtle->dpy, False); ///< Sync all changes free(names); subSubtleLogDebugSubtle("Publish: tags=%d\n", i); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/subtle.c0000644000175000017500000002526611770332063017754 0ustar formorerformorer /** * @package subtle * * @file Main functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/subtle.c,v 3190 2012/03/16 22:54:45 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include #include #include #include #include #include "subtle.h" #ifdef HAVE_EXECINFO_H #include #endif /* HAVE_EXECINFO_H */ SubSubtle *subtle = NULL; /* SubtleSignal {{{ */ static void SubtleSignal(int signum) { switch(signum) { case SIGCHLD: wait(NULL); break; case SIGHUP: if(subtle) subtle->flags |= SUB_SUBTLE_RELOAD; break; case SIGINT: if(subtle) subtle->flags &= ~SUB_SUBTLE_RUN; break; case SIGSEGV: { #ifdef HAVE_EXECINFO_H int i, frames = 0; void *callstack[10] = { 0 }; char **strings = NULL; frames = backtrace(callstack, 10); strings = backtrace_symbols(callstack, frames); printf("\n\nLast %d stack frames:\n", frames); for(i = 0; i < frames; i++) printf("%s\n", strings[i]); free(strings); #endif /* HAVE_EXECINFO_H */ printf("\nPlease report this bug at %s\n", PKG_BUGREPORT); abort(); } break; } } /* }}} */ /* SubtleUsage {{{ */ static void SubtleUsage(void) { printf("Usage: %s [OPTIONS]\n\n" \ "Options:\n" \ " -c, --config=FILE Load config\n" \ " -d, --display=DISPLAY Connect to DISPLAY\n" \ " -h, --help Show this help and exit\n" \ " -k, --check Check config syntax\n" \ " -n, --no-randr Disable RandR extension (required for Twinview)\n" \ " -r, --replace Replace current window manager\n" \ " -s, --sublets=DIR Load sublets from DIR\n" \ " -v, --version Show version info and exit\n" \ " -l, --level=LEVEL[,LEVEL] Set logging levels\n" \ " -D, --debug Print debugging messages\n" \ "\nPlease report bugs at %s\n", PKG_NAME, PKG_BUGREPORT); } /* }}} */ /* SubtleVersion {{{ */ static void SubtleVersion(void) { printf("%s %s - Copyright (c) 2005-2012 Christoph Kappel\n" \ "Released under the GNU General Public License\n" \ "Compiled for X%dR%d and Ruby %s\n", PKG_NAME, PKG_VERSION, X_PROTOCOL, X_PROTOCOL_REVISION, RUBY_VERSION); } /* }}} */ #ifdef DEBUG /* SubtleLevel {{{ */ static int SubtleLevel(const char *str) { int level = 0; char *tokens = NULL, *tok = NULL; tokens = strdup(str); tok = strtok((char *)tokens, ","); /* Parse levels */ while(tok) { if(0 == strncasecmp(tok, "warnings", 8)) level |= SUB_LOG_WARN; else if(0 == strncasecmp(tok, "error", 5)) level |= SUB_LOG_ERROR; else if(0 == strncasecmp(tok, "sublet", 6)) level |= SUB_LOG_SUBLET; else if(0 == strncasecmp(tok, "depcrecated", 11)) level |= SUB_LOG_DEPRECATED; else if(0 == strncasecmp(tok, "events", 6)) level |= SUB_LOG_EVENTS; else if(0 == strncasecmp(tok, "ruby", 4)) level |= SUB_LOG_RUBY; else if(0 == strncasecmp(tok, "xerror", 6)) level |= SUB_LOG_XERROR; else if(0 == strncasecmp(tok, "subtle", 6)) level |= SUB_LOG_SUBTLE; else if(0 == strncasecmp(tok, "debug", 4)) level |= SUB_LOG_DEBUG; tok = strtok(NULL, ","); } free(tokens); return level; } /* }}} */ #endif /* DEBUG */ /* Public */ /** subSubtleFind {{{ * @brief Find data with the context manager * @param[in] win A #Window * @param[in] id Context id * @return Returns found data pointer or \p NULL **/ XPointer * subSubtleFind(Window win, XContext id) { XPointer *data = NULL; return XCNOENT != XFindContext(subtle->dpy, win, id, (XPointer *)&data) ? data : NULL; } /* }}} */ /** subSubtleTime {{{ * @brief Get the current time in seconds * @return Returns time in seconds **/ time_t subSubtleTime(void) { struct timeval tv; gettimeofday(&tv, 0); return tv.tv_sec; } /* }}} */ /** subSubtleLog {{{ * @brief Print messages depending on type * @param[in] level Message level * @param[in] file File name * @param[in] line Line number * @param[in] format Message format * @param[in] ... Variadic arguments **/ void subSubtleLog(int level, const char *file, int line, const char *format, ...) { va_list ap; char buf[255]; #ifdef DEBUG if(!(subtle->loglevel & level)) return; #endif /* DEBUG */ /* Get variadic arguments */ va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); /* Print according to loglevel */ if(level & SUB_LOG_WARN) fprintf(stdout, " %s", buf); else if(level & SUB_LOG_ERROR) fprintf(stderr, " %s", buf); else if(level & SUB_LOG_SUBLET) fprintf(stderr, " %s", file, buf); else if(level & SUB_LOG_DEPRECATED) fprintf(stdout, " %s", buf); #ifdef DEBUG else if(level & SUB_LOG_EVENTS) fprintf(stderr, " %s:%d: %s", file, line, buf); else if(level & SUB_LOG_RUBY) fprintf(stderr, " %s:%d: %s", file, line, buf); else if(level & SUB_LOG_XERROR) fprintf(stderr, " %s", buf); else if(level & SUB_LOG_SUBTLE) fprintf(stderr, " %s:%d: %s", file, line, buf); else if(level & SUB_LOG_DEBUG) fprintf(stderr, " %s:%d: %s", file, line, buf); #endif /* DEBUG */ } /* }}} */ /** subSubtleFinish {{{ * @brief Finish subtle **/ void subSubtleFinish(void) { if(subtle) { if(subtle->dpy) XSync(subtle->dpy, False); ///< Sync before going on /* Handle hooks first */ if(subtle->hooks) { /* Hook: Exit */ subHookCall(SUB_HOOK_EXIT, NULL); /* Clear hooks first to stop calling */ subArrayClear(subtle->hooks, True); } /* Kill arrays */ if(subtle->clients) subArrayKill(subtle->clients, True); if(subtle->grabs) subArrayKill(subtle->grabs, True); if(subtle->gravities) subArrayKill(subtle->gravities, True); if(subtle->screens) subArrayKill(subtle->screens, True); if(subtle->sublets) subArrayKill(subtle->sublets, False); if(subtle->tags) subArrayKill(subtle->tags, True); if(subtle->trays) subArrayKill(subtle->trays, True); if(subtle->views) subArrayKill(subtle->views, True); if(subtle->hooks) subArrayKill(subtle->hooks, False); /* Reset styles to free fonts and substyles */ subStyleReset(&subtle->styles.all, 0); subStyleReset(&subtle->styles.views, 0); subStyleReset(&subtle->styles.title, 0); subStyleReset(&subtle->styles.sublets, 0); subStyleReset(&subtle->styles.separator, 0); subStyleReset(&subtle->styles.clients, 0); subStyleReset(&subtle->styles.subtle, 0); subEventFinish(); subRubyFinish(); subEwmhFinish(); subDisplayFinish(); free(subtle); } } /* }}} */ /* main {{{ */ int main(int argc, char *argv[]) { int c; char *display = NULL; struct sigaction sa; const struct option long_options[] = { { "config", required_argument, 0, 'c' }, { "display", required_argument, 0, 'd' }, { "help", no_argument, 0, 'h' }, { "check", no_argument, 0, 'k' }, { "no-randr", no_argument, 0, 'n' }, { "replace", no_argument, 0, 'r' }, { "sublets", required_argument, 0, 's' }, { "version", no_argument, 0, 'v' }, { "level", required_argument, 0, 'l' }, { "debug", no_argument, 0, 'D' }, { 0, 0, 0, 0} }; /* Create subtle */ subtle = (SubSubtle *)(subSharedMemoryAlloc(1, sizeof(SubSubtle))); subtle->flags |= (SUB_SUBTLE_XRANDR|SUB_SUBTLE_XINERAMA); subtle->loglevel = DEFAULT_LOGLEVEL; /* Parse arguments */ while(-1 != (c = getopt_long(argc, argv, "c:d:hknrs:vl:D", long_options, NULL))) { switch(c) { case 'c': subtle->paths.config = optarg; break; case 'd': display = optarg; break; case 'h': SubtleUsage(); return 0; case 'k': subtle->flags |= SUB_SUBTLE_CHECK; break; case 'n': subtle->flags &= ~SUB_SUBTLE_XRANDR; break; case 'r': subtle->flags |= SUB_SUBTLE_REPLACE; break; case 's': subtle->paths.sublets = optarg; break; case 'v': SubtleVersion(); return 0; #ifdef DEBUG case 'l': subtle->loglevel = SubtleLevel(optarg); break; case 'D': subtle->flags |= SUB_SUBTLE_DEBUG; subtle->loglevel |= DEBUG_LOGLEVEL; break; #else /* DEBUG */ case 'l': case 'D': printf("Please recompile %s with `debug=yes'\n", PKG_NAME); return 0; #endif /* DEBUG */ case '?': printf("Try `%s --help' for more information\n", PKG_NAME); return -1; } } /* Signal handler */ sa.sa_handler = SubtleSignal; sa.sa_flags = 0; memset(&sa.sa_mask, 0, sizeof(sigset_t)); ///< Avoid uninitialized values sigemptyset(&sa.sa_mask); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGSEGV, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); /* Load and check config only */ if(subtle->flags & SUB_SUBTLE_CHECK) { subRubyInit(); subRubyLoadConfig(); subRubyFinish(); free(subtle); ///< We just need to free this return 0; } /* Alloc arrays */ subtle->clients = subArrayNew(); subtle->grabs = subArrayNew(); subtle->gravities = subArrayNew(); subtle->hooks = subArrayNew(); subtle->screens = subArrayNew(); subtle->sublets = subArrayNew(); subtle->tags = subArrayNew(); subtle->trays = subArrayNew(); subtle->views = subArrayNew(); /* Init */ SubtleVersion(); subDisplayInit(display); subEwmhInit(); subScreenInit(); subRubyInit(); subGrabInit(); /* Load */ subRubyLoadConfig(); subRubyLoadSublets(); subRubyLoadPanels(); /* Display */ subDisplayConfigure(); subDisplayScan(); subEventLoop(); /* Restart if necessary */ if(subtle->flags & SUB_SUBTLE_RESTART) { subSubtleFinish(); printf("Restarting\n"); execvp(argv[0], argv); } else subSubtleFinish(); printf("Exit\n"); return 0; } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/grab.c0000644000175000017500000001374111770332063017364 0ustar formorerformorer /** * @package subtle * * @file Key functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/grab.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" static unsigned int numlockmask = 0; /* Public */ /** subGrabInit {{{ * @brief Init grabs and get modifiers **/ void subGrabInit(void) { XModifierKeymap *modmap = XGetModifierMapping(subtle->dpy); if(modmap && modmap->max_keypermod > 0) { const int modmasks[] = { ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask }; const KeyCode numlock = XKeysymToKeycode(subtle->dpy, XK_Num_Lock); int i, max = (sizeof(modmasks) / sizeof(int)) * modmap->max_keypermod; for(i = 0; i < max; i++) if(!modmap->modifiermap[i]) continue; else if(numlock && (modmap->modifiermap[i] == numlock)) numlockmask = modmasks[i / modmap->max_keypermod]; } if(modmap) XFreeModifiermap(modmap); subSubtleLogDebug("Init\n"); } /* }}} */ /** subGrabNew {{{ * @brief Create new grab * @param[in] keys Key chain * @param[out] duplicate Added twice * @return Returns a #SubGrab or \p NULL **/ SubGrab * subGrabNew(const char *keys, int *duplicate) { int mouse = False; unsigned int code = 0, state = 0; KeySym sym = NoSymbol; SubGrab *g = NULL; assert(keys); /* Parse keys */ if(NoSymbol != (sym = subSharedParseKey(subtle->dpy, keys, &code, &state, &mouse))) { /* Find or create new grab */ if(!(g = subGrabFind(code, state))) { g = GRAB(subSharedMemoryAlloc(1, sizeof(SubGrab))); g->code = code; g->state = state; g->flags = SUB_TYPE_GRAB|(mouse ? SUB_GRAB_MOUSE : SUB_GRAB_KEY); if(duplicate) *duplicate = False; } else if(duplicate) *duplicate = True; subSubtleLogDebugSubtle("New: type=%s, keys=%s, code=%03d, state=%02d\n", g->flags & SUB_GRAB_KEY ? "key" : "mouse", keys, g->code, g->state); } else subSubtleLogWarn("Cannot assign grab `%s'\n", keys); return g; } /* }}} */ /** subGrabFind {{{ * @brief Find grab * @param[in] code A key code * @param[in] state A key state * @return Returns a #SubGrab or \p NULL **/ SubGrab * subGrabFind(int code, unsigned int state) { SubGrab **ret = NULL, *gptr = NULL, g; /* Find grab via binary search */ g.code = code; g.state = (state & ~(LockMask|numlockmask)); gptr = &g; ret = (SubGrab **)bsearch(&gptr, subtle->grabs->data, subtle->grabs->ndata, sizeof(SubGrab *), subGrabCompare); return ret ? *ret : NULL; } /* }}} */ /** subGrabSet {{{ * @brief Grab keys for a window * @param[in] win Window * @param[in] mask Mask to select grabs to set **/ void subGrabSet(Window win, int mask) { if(win) { int i, j; const unsigned int states[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; /* Unbind click-to-focus grab */ if(subtle->flags & SUB_SUBTLE_FOCUS_CLICK && ROOT != win) XUngrabButton(subtle->dpy, AnyButton, AnyModifier, win); /* Bind grabs */ for(i = 0; i < subtle->grabs->ndata; i++) { SubGrab *g = GRAB(subtle->grabs->data[i]); /* Assign only grabs with action */ if(!(g->flags & (SUB_GRAB_CHAIN_LINK|SUB_GRAB_CHAIN_END)) && g->flags & mask) { /* FIXME: Ugly key/state grabbing */ for(j = 0; LENGTH(states) > j; j++) { if(g->flags & SUB_GRAB_KEY) { XGrabKey(subtle->dpy, g->code, g->state|states[j], ROOT, True, GrabModeAsync, GrabModeAsync); } else if(g->flags & SUB_GRAB_MOUSE) { XGrabButton(subtle->dpy, g->code - XK_Pointer_Button1, g->state|states[j], win, False, ButtonPressMask|ButtonReleaseMask, GrabModeSync, GrabModeAsync, None, None); } } } } } } /* }}} */ /** subGrabUnset {{{ * @brief Ungrab keys for a window * @param[in] win Window **/ void subGrabUnset(Window win) { XUngrabKey(subtle->dpy, AnyKey, AnyModifier, win); XUngrabButton(subtle->dpy, AnyButton, AnyModifier, win); /* Bind click-to-focus grab */ if(subtle->flags & SUB_SUBTLE_FOCUS_CLICK && ROOT != win) { XGrabButton(subtle->dpy, AnyButton, AnyModifier, win, False, ButtonPressMask>ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None); } } /* }}} */ /** subGrabCompare {{{ * @brief Compare two grabs * @param[in] a A #SubGrab * @param[in] b A #SubGrab * @return Returns the result of the comparison of both grabs * @retval -1 First is smaller * @retval 0 Both are equal * @retval 1 First is greater **/ int subGrabCompare(const void *a, const void *b) { int ret = 0; SubGrab *g1 = *(SubGrab **)a, *g2 = *(SubGrab **)b; assert(a && b); /* FIXME Complicated.. */ if(g1->code < g2->code) ret = -1; else if(g1->code == g2->code) { if(g1->state < g2->state) ret = -1; else if(g1->state == g2->state) ret = 0; else ret = 1; } else if(g1->code > g2->code) ret = 1; return ret; } /* }}} */ /** subGrabKill {{{ * @brief Kill grab * @param[in] g A #SubGrab **/ void subGrabKill(SubGrab *g) { assert(g); /* Clean certain types */ if(g->flags & (SUB_RUBY_DATA|SUB_GRAB_PROC) && g->data.num) { subRubyRelease(g->data.num); ///< Free ruby proc } else if(g->flags & (SUB_GRAB_SPAWN|SUB_GRAB_WINDOW_GRAVITY) && g->data.string) free(g->data.string); /* Delete keys */ if(g->keys) subArrayKill(g->keys, False); free(g); subSubtleLogDebugSubtle("Kill\n"); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/display.c0000644000175000017500000002425211770332063020115 0ustar formorerformorer /** * @package subtle * * @file Display functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/display.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include #include #include "subtle.h" /* DisplayClaim {{{ */ int DisplayClaim(void) { int success = True; char buf[10] = { 0 }; Atom session = None; Window owner = None; /* Get session atom */ snprintf(buf, sizeof(buf), "WM_S%d", DefaultScreen(subtle->dpy)); session = XInternAtom(subtle->dpy, buf, False); if((owner = XGetSelectionOwner(subtle->dpy, session))) { if(!(subtle->flags & SUB_SUBTLE_REPLACE)) { subSubtleLogError("Found a running window manager\n"); return False; } XSelectInput(subtle->dpy, owner, StructureNotifyMask); XSync(subtle->dpy, False); } /* Aquire session selection */ XSetSelectionOwner(subtle->dpy, session, subtle->windows.support, CurrentTime); /* Wait for previous window manager to exit */ if(XGetSelectionOwner(subtle->dpy, session) == subtle->windows.support) { if(owner) { int i; XEvent event; printf("Waiting for current window manager to exit\n"); for(i = 0; i < WAITTIME; i++) { if(XCheckWindowEvent(subtle->dpy, owner, StructureNotifyMask, &event) && DestroyNotify == event.type) return True; sleep(1); } subSubtleLogError("Giving up waiting for window managert\n"); success = False; } } else { subSubtleLogWarn("Failed replacing current window manager\n"); success = False; } return success; } /* }}} */ /* DisplayStyleToColor {{{ */ static void DisplayStyleToColor(SubStyle *s, unsigned long *colors, int *pos) { colors[(*pos)++] = s->fg; colors[(*pos)++] = s->bg; colors[(*pos)++] = s->top; colors[(*pos)++] = s->right; colors[(*pos)++] = s->bottom; colors[(*pos)++] = s->left; } /* }}} */ /* DisplayXError {{{ */ static int DisplayXError(Display *disp, XErrorEvent *ev) { #ifdef DEBUG if(subtle->loglevel & SUB_LOG_XERROR) { if(42 != ev->request_code) /* X_SetInputFocus */ { char error[255] = { 0 }; XGetErrorText(disp, ev->error_code, error, sizeof(error)); subSubtleLog(SUB_LOG_XERROR, __FILE__, __LINE__, "%s: win=%#lx, request=%d\n", error, ev->resourceid, ev->request_code); } } #endif /* DEBUG */ return 0; } /* }}} */ /* Public */ /** subDisplayInit {{{ * @brief Open connection to X server and create display * @param[in] display The display name as string **/ void subDisplayInit(const char *display) { XGCValues gvals; XSetWindowAttributes sattrs; unsigned long mask = 0; #if defined HAVE_X11_EXTENSIONS_XINERAMA_H || defined HAVE_X11_EXTENSIONS_XRANDR_H int event = 0, junk = 0; #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H HAVE_X11_EXTENSIONS_XRANDR_H */ assert(subtle); /* Set locale */ if(!setlocale(LC_CTYPE, "")) XSupportsLocale(); /* Connect to display and setup error handler */ if(!(subtle->dpy = XOpenDisplay(display))) { subSubtleLogError("Cannot connect to display `%s'\n", (display) ? display : ":0.0"); subSubtleFinish(); exit(-1); } /* Create supporting window */ subtle->windows.support = XCreateSimpleWindow(subtle->dpy, ROOT, -100, -100, 1, 1, 0, 0, 0); sattrs.override_redirect = True; sattrs.event_mask = PropertyChangeMask; XChangeWindowAttributes(subtle->dpy, subtle->windows.support, CWEventMask|CWOverrideRedirect, &sattrs); /* Claim and setup display */ if(!DisplayClaim()) { subSubtleFinish(); exit(-1); } XSetErrorHandler(DisplayXError); setenv("DISPLAY", DisplayString(subtle->dpy), True); ///< Set display for clients /* Create GCs */ gvals.fill_style = FillStippled; mask = GCFillStyle; subtle->gcs.stipple = XCreateGC(subtle->dpy, ROOT, mask, &gvals); gvals.function = GXinvert; gvals.subwindow_mode = IncludeInferiors; gvals.line_width = 3; mask = GCFunction|GCSubwindowMode|GCLineWidth; subtle->gcs.invert = XCreateGC(subtle->dpy, ROOT, mask, &gvals); gvals.line_width = 1; gvals.line_style = LineSolid; gvals.join_style = JoinMiter; gvals.cap_style = CapButt; gvals.fill_style = FillSolid; mask = GCLineWidth|GCLineStyle|GCJoinStyle|GCCapStyle| GCFillStyle; subtle->gcs.draw = XCreateGC(subtle->dpy, ROOT, mask, &gvals); /* Create cursors */ subtle->cursors.arrow = XCreateFontCursor(subtle->dpy, XC_left_ptr); subtle->cursors.move = XCreateFontCursor(subtle->dpy, XC_dotbox); subtle->cursors.resize = XCreateFontCursor(subtle->dpy, XC_sizing); /* Update root window */ sattrs.cursor = subtle->cursors.arrow; sattrs.event_mask = ROOTMASK; XChangeWindowAttributes(subtle->dpy, ROOT, CWCursor|CWEventMask, &sattrs); /* Create tray window */ subtle->windows.tray = XCreateSimpleWindow(subtle->dpy, ROOT, 0, 0, 1, 1, 0, 0, 0); sattrs.override_redirect = True; sattrs.event_mask = KeyPressMask|ButtonPressMask; XChangeWindowAttributes(subtle->dpy, subtle->windows.tray, CWOverrideRedirect|CWEventMask, &sattrs); /* Init screen width and height */ subtle->width = DisplayWidth(subtle->dpy, DefaultScreen(subtle->dpy)); subtle->height = DisplayHeight(subtle->dpy, DefaultScreen(subtle->dpy)); /* Check extensions */ #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H if(!XineramaQueryExtension(subtle->dpy, &event, &junk)) #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */ subtle->flags &= ~SUB_SUBTLE_XINERAMA; #ifdef HAVE_X11_EXTENSIONS_XRANDR_H if(!XRRQueryExtension(subtle->dpy, &event, &junk)) #endif /* HAVE_X11_EXTENSIONS_XRANDR_H */ subtle->flags &= ~SUB_SUBTLE_XRANDR; XSync(subtle->dpy, False); printf("Display (%s) is %dx%d\n", DisplayString(subtle->dpy), subtle->width, subtle->height); subSubtleLogDebugSubtle("Init\n"); } /* }}} */ /** subDisplayConfigure {{{ * @brief Configure display **/ void subDisplayConfigure(void) { XGCValues gvals; assert(subtle); /* Update GCs */ gvals.foreground = subtle->styles.subtle.fg; gvals.line_width = subtle->styles.clients.border.top; XChangeGC(subtle->dpy, subtle->gcs.stipple, GCForeground|GCLineWidth, &gvals); /* Update windows */ XSetWindowBackground(subtle->dpy, subtle->windows.tray, subtle->styles.subtle.top); /* Set background if set */ if(-1 != subtle->styles.subtle.bg) XSetWindowBackground(subtle->dpy, ROOT, subtle->styles.subtle.bg); XClearWindow(subtle->dpy, subtle->windows.tray); XClearWindow(subtle->dpy, ROOT); /* Update struts and panels */ subScreenResize(); subScreenUpdate(); XSync(subtle->dpy, False); ///< Sync all changes subSubtleLogDebugSubtle("Configure\n"); } /* }}} */ /** subDisplayScan {{{ * @brief Scan root window for clients **/ void subDisplayScan(void) { unsigned int i, nwins = 0; Window wroot = None, parent = None, *wins = NULL; assert(subtle); /* Scan for client windows */ XQueryTree(subtle->dpy, ROOT, &wroot, &parent, &wins, &nwins); for(i = 0; i < nwins; i++) { SubClient *c = NULL; XWindowAttributes attrs; XGetWindowAttributes(subtle->dpy, wins[i], &attrs); switch(attrs.map_state) { case IsViewable: if((c = subClientNew(wins[i]))) subArrayPush(subtle->clients, (void *)c); break; } } XFree(wins); subClientPublish(False); subSubtleLogDebugSubtle("Scan\n"); } /* }}} */ /** subDisplayPublish {{{ * @brief Update EWMH infos **/ void subDisplayPublish(void) { int pos = 0; unsigned long *colors; #define NCOLORS 54 /* Create color array */ colors = (unsigned long *)subSharedMemoryAlloc(NCOLORS, sizeof(unsigned long)); DisplayStyleToColor(&subtle->styles.title, colors, &pos); DisplayStyleToColor(&subtle->styles.views, colors, &pos); if(subtle->styles.focus) DisplayStyleToColor(subtle->styles.focus, colors, &pos); if(subtle->styles.urgent) DisplayStyleToColor(subtle->styles.urgent, colors, &pos); if(subtle->styles.occupied) DisplayStyleToColor(subtle->styles.occupied, colors, &pos); DisplayStyleToColor(&subtle->styles.sublets, colors, &pos); DisplayStyleToColor(&subtle->styles.separator, colors, &pos); colors[pos++] = subtle->styles.clients.fg; ///< Active colors[pos++] = subtle->styles.clients.bg; ///< Inactive colors[pos++] = subtle->styles.subtle.top; colors[pos++] = subtle->styles.subtle.bottom; colors[pos++] = subtle->styles.subtle.bg; colors[pos++] = subtle->styles.subtle.fg; ///< Stipple /* EWMH: Colors */ subEwmhSetCardinals(ROOT, SUB_EWMH_SUBTLE_COLORS, (long *)colors, NCOLORS); free(colors); XSync(subtle->dpy, False); ///< Sync all changes subSubtleLogDebugSubtle("Publish: colors=%d\n", NCOLORS); } /* }}} */ /** subDisplayFinish {{{ * @brief Close connection **/ void subDisplayFinish(void) { assert(subtle); if(subtle->dpy) { XSync(subtle->dpy, False); ///< Sync all changes /* Free cursors */ if(subtle->cursors.arrow) XFreeCursor(subtle->dpy, subtle->cursors.arrow); if(subtle->cursors.move) XFreeCursor(subtle->dpy, subtle->cursors.move); if(subtle->cursors.resize) XFreeCursor(subtle->dpy, subtle->cursors.resize); /* Free GCs */ if(subtle->gcs.stipple) XFreeGC(subtle->dpy, subtle->gcs.stipple); if(subtle->gcs.invert) XFreeGC(subtle->dpy, subtle->gcs.invert); if(subtle->gcs.draw) XFreeGC(subtle->dpy, subtle->gcs.draw); XDestroyWindow(subtle->dpy, subtle->windows.tray); XDestroyWindow(subtle->dpy, subtle->windows.support); XInstallColormap(subtle->dpy, DefaultColormap(subtle->dpy, SCRN)); XSetInputFocus(subtle->dpy, ROOT, RevertToPointerRoot, CurrentTime); XCloseDisplay(subtle->dpy); } subSubtleLogDebugSubtle("Finish\n"); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/array.c0000644000175000017500000001063511770332063017566 0ustar formorerformorer /** * @package subtle * * @file Array functions * @copyright 2005-2012 Christoph Kappel * @version $Id: src/subtle/array.c,v 3168 2012/01/03 16:02:50 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" /** subArrayNew {{{ * @brief Create new array and init it * @return Returns a #SubArray or \p NULL **/ SubArray * subArrayNew(void) { return (SubArray *)subSharedMemoryAlloc(1, sizeof(SubArray)); } /* }}} */ /** subArrayPush {{{ * @brief Push element to array * @param[in] a A #SubArray * @param[in] elem New element **/ void subArrayPush(SubArray *a, void *elem) { assert(a); if(elem) { a->data = (void **)subSharedMemoryRealloc(a->data, (a->ndata + 1) * sizeof(void *)); a->data[(a->ndata)++] = elem; } } /* }}} */ /** subArrayInsert {{{ * @brief Insert element at position * @param[in] a A #SubArray * @param[in] pos Position * @param[in] elem Array element **/ void subArrayInsert(SubArray *a, int pos, void *elem) { int i; assert(a && elem); /* Check boundaries */ if(pos < a->ndata) { a->ndata++; a->data = (void **)subSharedMemoryRealloc(a->data, a->ndata * sizeof(void *)); for(i = a->ndata - 1; i > pos; i--) a->data[i] = a->data[i - 1]; a->data[pos] = elem; } else subArrayPush(a, elem); } /* }}} */ /** subArrayRemove {{{ * @brief Remove element from array * @param[in] a A #SubArray * @param[in] elem Array element **/ void subArrayRemove(SubArray *a, void *elem) { int i, idx; assert(a && elem); if(0 <= (idx = subArrayIndex(a, elem))) { for(i = idx; i < a->ndata - 1; i++) a->data[i] = a->data[i + 1]; a->ndata--; a->data = (void **)subSharedMemoryRealloc(a->data, a->ndata * sizeof(void *)); } } /* }}} */ /** subArrayGet {{{ * @brief Get id after boundary check * @param[in] a A #SubArray * @param[in] id Array index * @return Returns an element or \p NULL **/ void * subArrayGet(SubArray *a, int id) { assert(a); return 0 <= id && id < a->ndata ? a->data[id] : NULL; } /* }}} */ /** subArrayIndex {{{ * @brief Find array id of element * @param[in] a A #SubArray * @param[in] elem Element * @return Returns found idx or \p -1 **/ int subArrayIndex(SubArray *a, void *elem) { int i; assert(a && elem); for(i = 0; i < a->ndata; i++) if(a->data[i] == elem) return i; return -1; } /* }}} */ /** subArraySort {{{ * @brief Sort array with given compare function * @param[in] a A #SubArray * @param[in] compar Compare function **/ void subArraySort(SubArray *a, int(*compar)(const void *a, const void *b)) { assert(a && compar); if(0 < a->ndata) qsort(a->data, a->ndata, sizeof(void *), compar); } /* }}} */ /** subArrayClear {{{ * @brief Delete all elements * @param[in] a A #SubArray * @param[in] clean Clean elements **/ void subArrayClear(SubArray *a, int clean) { if(a) { int i; SubClient *c = NULL; for(i = 0; clean && i < a->ndata; i++) { /* Check type and kill */ if((c = CLIENT(a->data[i]))) { if(c->flags & SUB_TYPE_CLIENT) subClientKill(c); else if(c->flags & SUB_TYPE_GRAB) subGrabKill(GRAB(c)); else if(c->flags & SUB_TYPE_GRAVITY) subGravityKill(GRAVITY(c)); else if(c->flags & SUB_TYPE_HOOK) subHookKill(HOOK(c)); else if(c->flags & SUB_TYPE_SCREEN) subScreenKill(SCREEN(c)); else if(c->flags & SUB_TYPE_STYLE) subStyleKill(STYLE(c)); else if(c->flags & SUB_TYPE_TAG) subTagKill(TAG(c)); else if(c->flags & SUB_TYPE_TRAY) subTrayKill(TRAY(c)); else if(c->flags & SUB_TYPE_VIEW) subViewKill(VIEW(c)); else if(c->flags & SUB_TYPE_PANEL) subPanelKill(PANEL(c)); } } if(a->data) free(a->data); a->data = NULL; a->ndata = 0; } } /* }}} */ /** subArrayKill {{{ * @brief Kill array with all elements * @param[in] a A #SubArray * @param[in] clean Clean to elements **/ void subArrayKill(SubArray *a, int clean) { if(a) { subArrayClear(a, clean); free(a); a = NULL; } } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/gravity.c0000644000175000017500000000770711770332063020143 0ustar formorerformorer /** * @package subtle * * @file Gravity functions * @copyright (c) 2005-2009 Christoph Kappel * @version $Id: src/subtle/gravity.c,v 3213 2012/05/31 13:42:04 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include "subtle.h" /* Method */ /** subGravityNew {{{ * @brief Create new gravity * @param[in] name Gravity name * @param[in] geom Geometry of gravity * @return Returns a new #SubGravity or \p NULL **/ SubGravity * subGravityNew(const char *name, XRectangle *geom) { SubGravity *g = NULL; assert(name && geom); /* Create gravity */ g = GRAVITY(subSharedMemoryAlloc(1, sizeof(SubGravity))); g->flags |= SUB_TYPE_GRAVITY; if(name) g->quark = XrmStringToQuark(name); ///< Create hash /* Sanitize values */ g->geom.x = MINMAX(geom->x, 0, 100); g->geom.y = MINMAX(geom->y, 0, 100); g->geom.width = MINMAX(geom->width, 1, 100); g->geom.height = MINMAX(geom->height, 1, 100); subSubtleLogDebugSubtle("New: name=%s, quark=%d, x=%d, y=%d," "width=%d, height=%d\n", name, g->quark, geom->x, geom->y, geom->width, geom->height); return g; } /* }}} */ /** subGravityGeometry {{{ * @brief Calculate geometry of gravity for bounds * @param[in] g A #SubGravity * @param[in] bounds A #XRectangle * @param[out] geom A #XRectangle **/ void subGravityGeometry(SubGravity *g, XRectangle *bounds, XRectangle *geom) { assert(g && bounds && geom); /* Calculate gravity size for bounds */ geom->x = bounds->x + (bounds->width * g->geom.x / 100); geom->y = bounds->y + (bounds->height * g->geom.y / 100); geom->width = (bounds->width * g->geom.width / 100); geom->height = (bounds->height * g->geom.height / 100); } /* }}} */ /** subGravityKill {{{ * @brief Kill gravity * @param[in] g A #SubGravity **/ void subGravityKill(SubGravity *g) { assert(g); free(g); subSubtleLogDebugSubtle("Kill\n"); } /* }}} */ /* All */ /** subGravityFind {{{ * @brief Find gravity * @param[in] name Gravity name * @param[in] quark Quark * @retval -1 Not found * @retval >0 Found id **/ int subGravityFind(const char *name, int quark) { int found = -1; if(0 < subtle->gravities->ndata) { int i, hash = 0; /* Get quark */ if(name) hash = XrmStringToQuark(name); else hash = quark; for(i = 0; i < subtle->gravities->ndata; i++) { SubGravity *g = GRAVITY(subtle->gravities->data[i]); /* Compare quarks */ if(g->quark == hash) { found = i; break; } } } return found; } /* }}} */ /** subGravityPublish {{{ * @brief Publish gravities **/ void subGravityPublish(void) { int i; char **gravities = NULL, buf[30] = { 0 }; SubGravity *g = NULL; assert(0 < subtle->gravities->ndata); /* Alloc space */ gravities = (char **)subSharedMemoryAlloc(subtle->gravities->ndata, sizeof(char *)); for(i = 0; i < subtle->gravities->ndata; i++) { g = GRAVITY(subtle->gravities->data[i]); /* Add gravity to list */ snprintf(buf, sizeof(buf), "%dx%d+%d+%d#%s", g->geom.x, g->geom.y, g->geom.width, g->geom.height, XrmQuarkToString(g->quark)); gravities[i] = (char *)subSharedMemoryAlloc(strlen(buf) + 1, sizeof(char)); strncpy(gravities[i], buf, strlen(buf)); } /* EWMH: Gravity list and geometries */ subSharedPropertySetStrings(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_GRAVITY_LIST), gravities, subtle->gravities->ndata); /* Tidy up */ for(i = 0; i < subtle->gravities->ndata; i++) free(gravities[i]); XSync(subtle->dpy, False); ///< Sync all changes free(gravities); subSubtleLogDebugSubtle("Publish: gravities=%d\n", subtle->gravities->ndata); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/subtle/screen.c0000644000175000017500000004721111770332063017727 0ustar formorerformorer /** * @package subtle * * @file Display functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/subtle/screen.c,v 3201 2012/04/12 14:01:20 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include "subtle.h" /* ScreenPublish {{{ */ static void ScreenPublish(void) { int i; long *workareas = NULL, *panels = NULL, *viewports = NULL; assert(subtle); /* EWMH: Workarea and screen panels */ workareas = (long *)subSharedMemoryAlloc(4 * subtle->screens->ndata, sizeof(long)); panels = (long *)subSharedMemoryAlloc(2 * subtle->screens->ndata, sizeof(long)); /* Collect data*/ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); /* Set workareas */ workareas[i * 4 + 0] = s->geom.x; workareas[i * 4 + 1] = s->geom.y; workareas[i * 4 + 2] = s->geom.width; workareas[i * 4 + 3] = s->geom.height; /* Set panels */ panels[i * 2 + 0] = s->flags & SUB_SCREEN_PANEL1 ? subtle->ph : 0; panels[i * 2 + 1] = s->flags & SUB_SCREEN_PANEL2 ? subtle->ph : 0; } subEwmhSetCardinals(ROOT, SUB_EWMH_NET_WORKAREA, workareas, 4 * subtle->screens->ndata); subEwmhSetCardinals(ROOT, SUB_EWMH_SUBTLE_SCREEN_PANELS, panels, 2 * subtle->screens->ndata); /* EWMH: Desktop viewport */ viewports = (long *)subSharedMemoryAlloc(2 * subtle->screens->ndata, sizeof(long)); ///< Calloc inits with zero - great subEwmhSetCardinals(ROOT, SUB_EWMH_NET_DESKTOP_VIEWPORT, viewports, 2 * subtle->screens->ndata); free(workareas); free(panels); free(viewports); XSync(subtle->dpy, False); ///< Sync all changes subSubtleLogDebugSubtle("Publish: screens=%d\n", subtle->screens->ndata); } /* }}} */ /* ScreenClear {{{ */ static void ScreenClear(SubScreen *s, unsigned long col) { /* Clear pixmap */ XSetForeground(subtle->dpy, subtle->gcs.draw, col); XFillRectangle(subtle->dpy, s->drawable, subtle->gcs.draw, 0, 0, s->base.width, subtle->ph); /* Draw stipple on panels */ if(s->flags & SUB_SCREEN_STIPPLE) { XGCValues gvals; gvals.stipple = s->stipple; XChangeGC(subtle->dpy, subtle->gcs.stipple, GCStipple, &gvals); XFillRectangle(subtle->dpy, s->drawable, subtle->gcs.stipple, 0, 0, s->base.width, subtle->ph); } } /* }}} */ /* Public */ /** subScreenInit {{{ * @brief Init screens **/ void subScreenInit(void) { SubScreen *s = NULL; #ifdef HAVE_X11_EXTENSIONS_XRANDR_H /* Check both but prefer xrandr */ if(subtle->flags & SUB_SUBTLE_XRANDR) { XRRScreenResources *res = NULL; if((res = XRRGetScreenResourcesCurrent(subtle->dpy, ROOT))) { int i; XRRCrtcInfo *crtc = NULL; /* Query screens */ for(i = 0; i < res->ncrtc; i++) { if((crtc = XRRGetCrtcInfo(subtle->dpy, res, res->crtcs[i]))) { /* Create new screen if crtc is enabled */ if(None != crtc->mode && (s = subScreenNew(crtc->x, crtc->y, crtc->width, crtc->height))) subArrayPush(subtle->screens, (void *)s); XRRFreeCrtcInfo(crtc); } } XRRFreeScreenResources(res); } } #endif /* HAVE_X11_EXTENSIONS_XRANDR_H */ #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H if(subtle->flags & SUB_SUBTLE_XINERAMA && 0 == subtle->screens->ndata && XineramaIsActive(subtle->dpy)) { int i, n = 0; XineramaScreenInfo *info = NULL; /* Query screens */ if((info = XineramaQueryScreens(subtle->dpy, &n))) { for(i = 0; i < n; i++) { /* Create new screen */ if((s = subScreenNew(info[i].x_org, info[i].y_org, info[i].width, info[i].height))) subArrayPush(subtle->screens, (void *)s); } XFree(info); } } #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */ /* Create default screen */ if(0 == subtle->screens->ndata) { /* Create new screen */ if((s = subScreenNew(0, 0, subtle->width, subtle->height))) subArrayPush(subtle->screens, (void *)s); } printf("Running on %d screen(s)\n", subtle->screens->ndata); ScreenPublish(); subScreenPublish(); subSubtleLogDebugSubtle("init=screen\n"); } /* }}} */ /** subScreenNew {{{ * @brief Create a new view * @param[in] x X position of screen * @param[in] y y position of screen * @param[in] width Width of screen * @param[in] height Height of screen * @return Returns a #SubScreen or \p NULL **/ SubScreen * subScreenNew(int x, int y, unsigned int width, unsigned int height) { SubScreen *s = NULL; XSetWindowAttributes sattrs; unsigned long mask = 0; /* Create screen */ s = SCREEN(subSharedMemoryAlloc(1, sizeof(SubScreen))); s->flags = SUB_TYPE_SCREEN; s->geom.x = x; s->geom.y = y; s->geom.width = width; s->geom.height = height; s->base = s->geom; ///< Backup size s->viewid = subtle->screens->ndata; ///< Init /* Create panel windows */ sattrs.event_mask = ButtonPressMask|EnterWindowMask| LeaveWindowMask|ExposureMask; sattrs.override_redirect = True; sattrs.background_pixmap = ParentRelative; mask = CWEventMask|CWOverrideRedirect|CWBackPixmap; s->panel1 = XCreateWindow(subtle->dpy, ROOT, 0, 1, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, mask, &sattrs); s->panel2 = XCreateWindow(subtle->dpy, ROOT, 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, mask, &sattrs); XSaveContext(subtle->dpy, s->panel1, SCREENID, (void *)s); XSaveContext(subtle->dpy, s->panel2, SCREENID, (void *)s); subSubtleLogDebugSubtle("New: x=%d, y=%d, width=%u, height=%u\n", s->geom.x, s->geom.y, s->geom.width, s->geom.height); return s; } /* }}} */ /** subScreenFind {{{ * @brief Find screen by coordinates * @param[in] x X coordinate * @param[in] y Y coordinate * @param[inout] sid Screen id * @return Returns a #SubScreen **/ SubScreen * subScreenFind(int x, int y, int *sid) { int i; SubScreen *ret = SCREEN(subtle->screens->data[0]); /* Check screens */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); /* Check if coordinates are in screen rects */ if((x >= s->base.x && x < s->base.x + s->base.width) && (y >= s->base.y && y < s->base.y + s->base.height)) { ret = s; if(sid) *sid = i; break; } } subSubtleLogDebugSubtle("Find\n"); return ret; } /* }}} */ /** subScreenCurrent {{{ * @brief Find screen by coordinates * @param[inout] sid Screen id * @return Current #SubScreen or \p NULL **/ SubScreen * subScreenCurrent(int *sid) { SubScreen *ret = NULL; /* Check if there is only one screen */ if(1 == subtle->screens->ndata) { if(sid) *sid = 0; ret = SCREEN(subtle->screens->data[0]); } else { int rx = 0, ry = 0, x = 0, y = 0; Window root = None, win = None; unsigned int mask = 0; /* Get current screen */ XQueryPointer(subtle->dpy, ROOT, &root, &win, &rx, &ry, &x, &y, &mask); ret = subScreenFind(rx, ry, sid); } subSubtleLogDebugSubtle("Current\n"); return ret; } /* }}} */ /** subScreenConfigure {{{ * @brief Configure screens **/ void subScreenConfigure(void) { int i; SubScreen *s = NULL; SubView *v = NULL; /* Reset visible tags, views and available clients */ subtle->visible_tags = 0; subtle->visible_views = 0; subtle->client_tags = 0; /* Either check each client or just get visible clients */ if(0 < subtle->clients->ndata) { int j; /* Check each client */ for(i = 0; i < subtle->clients->ndata; i++) { SubClient *c = CLIENT(subtle->clients->data[i]); int gravityid = 0, screenid = 0, viewid = 0, visible = 0; /* Ignore dead or just iconified clients */ if(c->flags & SUB_CLIENT_DEAD) continue; /* Set available client tags to ease lookups */ subtle->client_tags |= c->tags; /* Check view of each screen */ for(j = 0; j < subtle->screens->ndata; j++) { s = SCREEN(subtle->screens->data[j]); v = VIEW(subtle->views->data[s->viewid]); /* Set visible tags and views to ease lookups */ subtle->visible_tags |= v->tags; subtle->visible_views |= (1L << (s->viewid + 1)); /* Find visible clients */ if(VISIBLETAGS(c, v->tags)) { /* Keep screen when sticky */ if(c->flags & SUB_CLIENT_MODE_STICK) { /* Keep gravity from sticky screen/view and not the one * of the current screen/view in loop */ s = SCREEN(subtle->screens->data[c->screenid]); screenid = c->screenid; } else screenid = j; viewid = s->viewid; gravityid = c->gravities[s->viewid]; visible++; } } /* After all screens are checked.. */ if(0 < visible) { /* Update client */ subClientArrange(c, gravityid, screenid); XMapWindow(subtle->dpy, c->win); subEwmhSetWMState(c->win, NormalState); /* Warp after gravity and screen have been set if not disabled */ if(c->flags & SUB_CLIENT_MODE_URGENT && !(subtle->flags & SUB_SUBTLE_SKIP_URGENT_WARP) && !(subtle->flags & SUB_SUBTLE_SKIP_WARP)) subClientWarp(c); /* EWMH: Desktop, screen */ subEwmhSetCardinals(c->win, SUB_EWMH_NET_WM_DESKTOP, (long *)&viewid, 1); subEwmhSetCardinals(c->win, SUB_EWMH_SUBTLE_CLIENT_SCREEN, (long *)&screenid, 1); } else ///< Unmap other windows { c->flags |= SUB_CLIENT_UNMAP; ///< Ignore next unmap subEwmhSetWMState(c->win, WithdrawnState); XUnmapWindow(subtle->dpy, c->win); } } } else { /* Check views of each screen */ for(i = 0; i < subtle->screens->ndata; i++) { s = SCREEN(subtle->screens->data[i]); v = VIEW(subtle->views->data[s->viewid]); /* Set visible tags and views to ease lookups */ subtle->visible_tags |= v->tags; subtle->visible_views |= (1L << (s->viewid + 1)); } } /* EWMH: Visible tags, views */ subEwmhSetCardinals(ROOT, SUB_EWMH_SUBTLE_VISIBLE_TAGS, (long *)&subtle->visible_tags, 1); subEwmhSetCardinals(ROOT, SUB_EWMH_SUBTLE_VISIBLE_VIEWS, (long *)&subtle->visible_views, 1); XSync(subtle->dpy, False); ///< Sync before going on /* Hook: Configure */ subHookCall(SUB_HOOK_TILE, NULL); subSubtleLogDebugSubtle("Configure\n"); } /* }}} */ /** subScreenUpdate {{{ * @brief Update screens **/ void subScreenUpdate(void) { int i; /* Update screens */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); SubPanel *p = NULL; int j, npanel = 0, center = False, offset = 0; int x[4] = { 0 }, nspacer[4] = { 0 }; ///< Waste ints but it's easier for the algo int sw[4] = { 0 }, fix[4] = { 0 }, width[4] = { 0 }, spacer[4] = { 0 }; /* Pass 1: Collect width for spacer sizes */ for(j = 0; s->panels && j < s->panels->ndata; j++) { p = PANEL(s->panels->data[j]); subPanelUpdate(p); /* Check flags */ if(p->flags & SUB_PANEL_HIDDEN) continue; if(0 == npanel && p->flags & SUB_PANEL_BOTTOM) { npanel = 1; center = False; } if(p->flags & SUB_PANEL_CENTER) center = !center; /* Offset selects panel variables for either center or not */ offset = center ? npanel + 2 : npanel; if(p->flags & SUB_PANEL_SPACER1) spacer[offset]++; if(p->flags & SUB_PANEL_SPACER2) spacer[offset]++; if(p->flags & SUB_PANEL_SEPARATOR1 && subtle->styles.separator.separator) width[offset] += subtle->styles.separator.separator->width; if(p->flags & SUB_PANEL_SEPARATOR2 && subtle->styles.separator.separator) width[offset] += subtle->styles.separator.separator->width; width[offset] += p->width; } /* Calculate spacer and fix sizes */ for(j = 0; j < 4; j++) { if(0 < spacer[j]) { sw[j] = (s->base.width - width[j]) / spacer[j]; fix[j] = s->base.width - (width[j] + spacer[j] * sw[j]); } } /* Pass 2: Move and resize windows */ for(j = 0, npanel = 0, center = False; s->panels && j < s->panels->ndata; j++) { p = PANEL(s->panels->data[j]); /* Check flags */ if(p->flags & SUB_PANEL_HIDDEN) continue; if(0 == npanel && p->flags & SUB_PANEL_BOTTOM) { /* Reset for new panel */ npanel = 1; nspacer[0] = 0; nspacer[2] = 0; x[0] = 0; x[2] = 0; center = False; } if(p->flags & SUB_PANEL_CENTER) center = !center; /* Offset selects panel variables for either center or not */ offset = center ? npanel + 2 : npanel; /* Set start position of centered panel items */ if(center && 0 == x[offset]) x[offset] = (s->base.width - width[offset]) / 2; /* Add separator before panel item */ if(p->flags & SUB_PANEL_SEPARATOR1 && subtle->styles.separator.separator) x[offset] += subtle->styles.separator.separator->width; /* Add spacer before item */ if(p->flags & SUB_PANEL_SPACER1) { x[offset] += sw[offset]; /* Increase last spacer size by rounding fix */ if(++nspacer[offset] == spacer[offset]) x[offset] += fix[offset]; } /* Set panel position */ if(p->flags & SUB_PANEL_TRAY) XMoveWindow(subtle->dpy, subtle->windows.tray, x[offset], 0); p->x = x[offset]; /* Add separator after panel item */ if(p->flags & SUB_PANEL_SEPARATOR2 && subtle->styles.separator.separator) x[offset] += subtle->styles.separator.separator->width; /* Add spacer after item */ if(p->flags & SUB_PANEL_SPACER2) { x[offset] += sw[offset]; /* Increase last spacer size by rounding fix */ if(++nspacer[offset] == spacer[offset]) x[offset] += fix[offset]; } x[offset] += p->width; } } subSubtleLogDebugSubtle("Update\n"); } /* }}} */ /** subScreenRender {{{ * @brief Render screens **/ void subScreenRender(void) { int i, j; /* Render all screens */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); Window panel = s->panel1; ScreenClear(s, subtle->styles.subtle.top); /* Render panel items */ for(j = 0; s->panels && j < s->panels->ndata; j++) { SubPanel *p = PANEL(s->panels->data[j]); if(p->flags & SUB_PANEL_HIDDEN) continue; if(panel != s->panel2 && p->flags & SUB_PANEL_BOTTOM) { XCopyArea(subtle->dpy, s->drawable, panel, subtle->gcs.draw, 0, 0, s->base.width, subtle->ph, 0, 0); ScreenClear(s, subtle->styles.subtle.bottom); panel = s->panel2; } subPanelRender(p, s->drawable); } XCopyArea(subtle->dpy, s->drawable, panel, subtle->gcs.draw, 0, 0, s->base.width, subtle->ph, 0, 0); } XSync(subtle->dpy, False); ///< Sync before going on subSubtleLogDebugSubtle("Render\n"); } /* }}} */ /** subScreenResize {{{ * @brief Resize screens **/ void subScreenResize(void) { int i; assert(subtle); /* Update screens */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); /* Add strut */ s->geom.x = s->base.x + subtle->styles.subtle.padding.left; s->geom.y = s->base.y + subtle->styles.subtle.padding.top; s->geom.width = s->base.width - subtle->styles.subtle.padding.left - subtle->styles.subtle.padding.right; s->geom.height = s->base.height - subtle->styles.subtle.padding.top - subtle->styles.subtle.padding.bottom; /* Update panels */ if(s->flags & SUB_SCREEN_PANEL1) { XMoveResizeWindow(subtle->dpy, s->panel1, s->base.x, s->base.y, s->base.width, subtle->ph); XMapRaised(subtle->dpy, s->panel1); /* Update height */ s->geom.y += subtle->ph; s->geom.height -= subtle->ph; } else XUnmapWindow(subtle->dpy, s->panel1); if(s->flags & SUB_SCREEN_PANEL2) { XMoveResizeWindow(subtle->dpy, s->panel2, s->base.x, s->base.y + s->base.height - subtle->ph, s->base.width, subtle->ph); XMapRaised(subtle->dpy, s->panel2); /* Update height */ s->geom.height -= subtle->ph; } else XUnmapWindow(subtle->dpy, s->panel2); /* Create/update drawable for double buffering */ if(s->drawable) XFreePixmap(subtle->dpy, s->drawable); s->drawable = XCreatePixmap(subtle->dpy, ROOT, s->base.width, subtle->ph, XDefaultDepth(subtle->dpy, DefaultScreen(subtle->dpy))); } ScreenPublish(); subSubtleLogDebugSubtle("Resize\n"); } /* }}} */ /** subScreenWarp {{{ * @brief warp pointer to screen * @param[in] s A #SubScreen **/ void subScreenWarp(SubScreen *s) { assert(s); /* Move pointer to screen center */ XWarpPointer(subtle->dpy, None, ROOT, 0, 0, s->geom.x, s->geom.y, s->geom.x + s->geom.width / 2, s->geom.y + s->geom.height / 2); subSubtleLogDebugSubtle("Warp\n"); } /* }}} */ /** SubScreenKill {{{ * @brief Kill a screen * @param[in] s A #SubScreem **/ void subScreenKill(SubScreen *s) { assert(s); if(s->panels) subArrayKill(s->panels, True); /* Destroy panel windows */ if(s->panel1) { XDeleteContext(subtle->dpy, s->panel1, SCREENID); XDestroyWindow(subtle->dpy, s->panel1); } if(s->panel2) { XDeleteContext(subtle->dpy, s->panel2, SCREENID); XDestroyWindow(subtle->dpy, s->panel2); } /* Destroy drawable */ if(s->drawable) XFreePixmap(subtle->dpy, s->drawable); free(s); subSubtleLogDebugSubtle("Kill\n"); } /* }}} */ /* All */ /** subScreenPublish {{{ * @brief Publish screens **/ void subScreenPublish(void) { int i; long *views = NULL; assert(subtle); /* EWMH: Views per screen */ views = (long *)subSharedMemoryAlloc(subtle->screens->ndata, sizeof(long)); /* Collect views */ for(i = 0; i < subtle->screens->ndata; i++) views[i] = SCREEN(subtle->screens->data[i])->viewid; subEwmhSetCardinals(ROOT, SUB_EWMH_SUBTLE_SCREEN_VIEWS, views, subtle->screens->ndata); free(views); XSync(subtle->dpy, False); ///< Sync all changes subSubtleLogDebugSubtle("Publish: screens=%d\n", subtle->screens->ndata); } /* }}} */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/shared/0000755000175000017500000000000011770527221016251 5ustar formorerformorersubtle-0.11.3224-xi/src/shared/shared.h0000644000175000017500000001667611770332063017706 0ustar formorerformorer /** * @package subtle * * @file Header file * @copyright Copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/shared/shared.h,v 3204 2012/05/22 21:15:06 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #ifndef SHARED_H #define SHARED_H 1 /* Includes {{{ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #ifdef HAVE_X11_XFT_XFT_H #include #endif /* HAVE_X11_XFT_XFT_H */ /* }}} */ /* Macros {{{ */ #define SEPARATOR "^" ///< Color separator #define LENGTH(a) (sizeof(a) / sizeof(a[0])) ///< Array length #define RINT(r) printf("<%s:%d> %s: x=%d, y=%d, width=%d, height=%d\n", \ __FILE__, __LINE__, #r, r.x, r.y, r.width, r.height); ///< Print a XRectangle #define DEFFONT "-*-*-*-*-*-*-14-*-*-*-*-*-*-*" ///< Default font #define DATA(d) ((SubData)d) ///< Cast to SubData #define FONT(f) ((SubFont *)f) ///< Cast to SubFont #define TEXT(t) ((SubText *)t) ///< Cast to SubText #define ITEM(i) ((SubTextItem *)i) ///< Cast to SubTextItem /* }}} */ /* Flags {{{ */ /* View select flags */ #define SUB_VIEW_NEXT 0L ///< View next #define SUB_VIEW_PREV 1L ///< View prev /* EWMH flags */ #define SUB_EWMH_FULL (1L << 0) ///< EWMH full flag #define SUB_EWMH_FLOAT (1L << 1) ///< EWMH float flag #define SUB_EWMH_STICK (1L << 2) ///< EWMH stick flag #define SUB_EWMH_RESIZE (1L << 3) ///< EWMH resize flag #define SUB_EWMH_URGENT (1L << 4) ///< EWMH urgent flag #define SUB_EWMH_ZAPHOD (1L << 5) ///< EWMH zaphod flag #define SUB_EWMH_FIXED (1L << 6) ///< EWMH fixed flag #define SUB_EWMH_CENTER (1L << 7) ///< EWMH center flag #define SUB_EWMH_BORDERLESS (1L << 8) ///< EWMH fixed flag #define SUB_EWMH_VISIBLE (1L << 9) ///< EWMH visible flag #define SUB_EWMH_HIDDEN (1L << 10) ///< EWMH hidden flag #define SUB_EWMH_HORZ (1L << 11) ///< EWMH horizontal flag #define SUB_EWMH_VERT (1L << 12) ///< EWMH vertical flag /* Match types flags */ #define SUB_MATCH_NAME (1L << 0) ///< Match SUBTLE_NAME #define SUB_MATCH_INSTANCE (1L << 1) ///< Match instance of WM_CLASS #define SUB_MATCH_CLASS (1L << 2) ///< Match class of WM_CLASS #define SUB_MATCH_GRAVITY (1L << 3) ///< Match gravity #define SUB_MATCH_ROLE (1L << 4) ///< Match window role #define SUB_MATCH_PID (1L << 5) ///< Match pid #define SUB_MATCH_EXACT (1L << 6) ///< Match exact string /* }}} */ /* Typedefs {{{ */ typedef union subdata_t /* {{{ */ { unsigned long num; ///< Data number char *string; ///< Data string } SubData; /* }}} */ typedef struct subfont_t /* {{{ */ { int y, height; ///< Font y, height XFontSet xfs; ///< Font set #ifdef HAVE_X11_XFT_XFT_H XftFont *xft; ///< Font XFT font XftDraw *draw; ///< Font XFT draw #endif /* HAVE_X11_XFT_XFT_H */ } SubFont; /* }}} */ typedef union submessagedata_t /* {{{ */ { char b[20]; ///< MessageData char short s[10]; ///< MessageData short long l[5]; ///< MessageData long } SubMessageData; /* }}} */ /* }}} */ /* Memory {{{ */ void *subSharedMemoryAlloc(size_t n, size_t size); ///< Allocate memory void *subSharedMemoryRealloc(void *mem, size_t size); ///< Reallocate memory /* }}} */ /* Regex {{{ */ regex_t *subSharedRegexNew(char *pattern); ///< Create new regex int subSharedRegexMatch(regex_t *preg, char *string); ///< Check if string matches preg void subSharedRegexKill(regex_t *preg); ///< Kill regex /* }}} */ /* Property {{{ */ char *subSharedPropertyGet(Display *disp, Window win, Atom type, Atom prop, unsigned long *size); ///< Get window property char **subSharedPropertyGetStrings(Display *disp, Window win, Atom prop, int *nlist); ///< Get window property list void subSharedPropertySetStrings(Display *disp, Window win, Atom prop, char **list, int nlist); ///< Set window property list void subSharedPropertyName(Display *disp, Window win, char **name, char *fallback); ///< Get window name void subSharedPropertyClass(Display *disp, Window win, char **inst, char **klass); ///< Get window class void subSharedPropertyGeometry(Display *disp, Window win, XRectangle *geometry); ///< Get window geometry void subSharedPropertyDelete(Display *disp, Window win, Atom prop); ///< Delete window property /* }}} */ /* Draw {{{ */ void subSharedDrawIcon(Display *disp, GC gc, Window win, int x, int y, int width, int height, long fg, long bg, Pixmap pixmap, int bitmap); ///< Draw icons void subSharedDrawString(Display *disp, GC gc, SubFont *f, Window win, int x, int y, long fg, long bg, const char *text, int len); ///< Draw text /* }}} */ /* Font {{{ */ SubFont *subSharedFontNew(Display *disp, const char *name); ///< Create font void subSharedFontKill(Display *disp, SubFont *f); ///< Kill font /* }}} */ /* Misc {{{ */ unsigned long subSharedParseColor(Display *disp, char *name); ///< Parse color KeySym subSharedParseKey(Display *disp, const char *key, unsigned int *code, unsigned int *state, int *mouse); ///< Parse keys pid_t subSharedSpawn(char *cmd); ///< Spawn command int subSharedStringWidth(Display *disp, SubFont *f, const char *text, int len, int *left, int *right, int center); ///< Get text width /* }}} */ #ifndef SUBTLE /* Message {{{ */ int subSharedMessage(Display *disp, Window win, char *type, SubMessageData data, int format, int xsync); ///< Send client message /* }}} */ #endif /* SUBTLE */ #endif /* SHARED_H */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/src/shared/shared.c0000644000175000017500000004330711770332063017670 0ustar formorerformorer /** * @package subtle * * @file Shared functions * @copyright (c) 2005-2012 Christoph Kappel * @version $Id: src/shared/shared.c,v 3204 2012/05/22 21:15:06 unexist $ * * This program can be distributed under the terms of the GNU GPLv2. * See the file COPYING for details. **/ #include #include #include #include #include "shared.h" /* Memory */ /** subSharedMemoryAlloc {{{ * @brief Alloc memory and check result * @param[in] n Number of elements * @param[in] size Size of the memory block * @return Returns new memory block or \p NULL **/ void * subSharedMemoryAlloc(size_t n, size_t size) { void *mem = NULL; /* Check result */ if(!(mem = calloc(n, size))) { fprintf(stderr, " Failed allocating memory\n"); abort(); } return mem; } /* }}} */ /** subSharedMemoryRealloc {{{ * @brief Realloc memory and check result * @param[in] mem Memory block * @param[in] size Size of the memory block * @return Returns new memory block or \p NULL **/ void * subSharedMemoryRealloc(void *mem, size_t size) { /* Check result */ if(!(mem = realloc(mem, size))) fprintf(stderr, " Memory has been freed. Expected?\n"); return mem; } /* }}} */ /* Regex */ /** subSharedRegexNew {{{ * @brief Create new regex * @param[in] pattern Regex * @return Returns a #regex_t or \p NULL **/ regex_t * subSharedRegexNew(char *pattern) { int ecode = 0; regex_t *preg = NULL; OnigErrorInfo einfo; assert(pattern); /* Create onig regex */ ecode = onig_new(&preg, (UChar *)pattern, (UChar *)(pattern + strlen(pattern)), ONIG_OPTION_EXTEND|ONIG_OPTION_SINGLELINE|ONIG_OPTION_IGNORECASE, ONIG_ENCODING_ASCII, ONIG_SYNTAX_RUBY, &einfo); /* Check for compile errors */ if(ecode) { UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN] = { 0 }; onig_error_code_to_str((UChar*)ebuf, ecode, &einfo); fprintf(stderr, " Failed compiling regex `%s': %s\n", pattern, ebuf); free(preg); return NULL; } return preg; } /* }}} */ /** subSharedRegexMatch {{{ * @brief Check if string match preg * @param[in] preg A #regex_t * @param[in] string String * @retval 1 If string matches preg * @retval 0 If string doesn't match **/ int subSharedRegexMatch(regex_t *preg, char *string) { assert(preg); return ONIG_MISMATCH != onig_match(preg, (UChar *)string, (UChar *)(string + strlen(string)), (UChar *)string, NULL, ONIG_OPTION_NONE); } /* }}} */ /** subSharedRegexKill {{{ * @brief Kill preg * @param[in] preg #regex_t **/ void subSharedRegexKill(regex_t *preg) { assert(preg); onig_free(preg); } /* }}} */ /* Property */ /** subSharedPropertyGet {{{ * @brief Get window property * @param[in] disp Display * @param[in] win Client window * @param[in] type Property type * @param[in] prop Property * @param[inout] size Size of the property * return Returns the property **/ char * subSharedPropertyGet(Display *disp, Window win, Atom type, Atom prop, unsigned long *size) { int format = 0; unsigned long nitems = 0, bytes = 0; unsigned char *data = NULL; Atom rtype = None; assert(win); /* Get property */ if(Success != XGetWindowProperty(disp, win, prop, 0L, 4096, False, type, &rtype, &format, &nitems, &bytes, &data)) return NULL; /* Check result */ if(type != rtype) { XFree(data); return NULL; } if(size) *size = nitems; return (char *)data; } /* }}} */ /** subSharedPropertyGetStrings {{{ * @brief Get property list * @param[in] disp Display * @param[in] win Client window * @param[in] prop Property * @param[inout] nlist Size of the property list * return Returns the property list **/ char ** subSharedPropertyGetStrings(Display *disp, Window win, Atom prop, int *nlist) { char **list = NULL; XTextProperty text; assert(win && nlist); /* Check UTF8 and XA_STRING */ if((XGetTextProperty(disp, win, &text, prop) || XGetTextProperty(disp, win, &text, XA_STRING)) && text.nitems) { XmbTextPropertyToTextList(disp, &text, &list, nlist); XFree(text.value); } return list; } /* }}} */ /** subSharedPropertySetStrings {{{ * @brief Convert list to multibyte and set property * @param[in] disp Display * @param[in] win Window * @param[in] prop Property * @param[in] list String list * @param[in] nsize Number of elements * return Returns the property list **/ void subSharedPropertySetStrings(Display *disp, Window win, Atom prop, char **list, int nlist) { XTextProperty text; /* Convert list to multibyte text property */ XmbTextListToTextProperty(disp, list, nlist, XUTF8StringStyle, &text); XSetTextProperty(disp, win, &text, prop); XFree(text.value); } /* }}} */ /** subSharedPropertyName {{{ * @brief Get window name * @warning Must be free'd * @param[in] disp Display * @param[in] win A #Window * @param[inout] name Window WM_NAME * @param[in] fallback Fallback name **/ void subSharedPropertyName(Display *disp, Window win, char **name, char *fallback) { char **list = NULL; XTextProperty text; /* Get text property */ XGetTextProperty(disp, win, &text, XInternAtom(disp, "_NET_WM_NAME", False)); if(0 == text.nitems) { XGetTextProperty(disp, win, &text, XA_WM_NAME); if(0 == text.nitems) { *name = strdup(fallback); return; } } /* Handle encoding */ if(XA_STRING == text.encoding) { *name = strdup((char *)text.value); } else ///< Utf8 string { int nlist = 0; /* Convert text property */ if(Success == XmbTextPropertyToTextList(disp, &text, &list, &nlist) && list) { if(0 < nlist && *list) { /* FIXME strdup() allocates not enough memory to hold string */ *name = subSharedMemoryAlloc(text.nitems + 2, sizeof(char)); strncpy(*name, *list, text.nitems); } XFreeStringList(list); } } if(text.value) XFree(text.value); /* Fallback */ if(!*name) *name = strdup(fallback); } /* }}} */ /** subSharedPropertyClass {{{ * @brief Get window class * @warning Must be free'd * @param[in] disp Display * @param[in] win A #Window * @param[inout] inst Window instance name * @param[inout] klass Window class name **/ void subSharedPropertyClass(Display *disp, Window win, char **inst, char **klass) { int size = 0; char **klasses = NULL; assert(win); klasses = subSharedPropertyGetStrings(disp, win, XA_WM_CLASS, &size); /* Sanitize instance/class names */ if(inst) *inst = strdup(0 < size ? klasses[0] : "subtle"); if(klass) *klass = strdup(1 < size ? klasses[1] : "subtle"); if(klasses) XFreeStringList(klasses); } /* }}} */ /** subSharedPropertyGeometry {{{ * @brief Get window geometry * @param[in] disp Display * @param[in] win A #Window * @param[inout] geometry A #XRectangle **/ void subSharedPropertyGeometry(Display *disp, Window win, XRectangle *geometry) { Window root = None; unsigned int bw = 0, depth = 0; XRectangle r = { 0 }; assert(win && geometry); XGetGeometry(disp, win, &root, (int *)&r.x, (int *)&r.y, (unsigned int *)&r.width, (unsigned int *)&r.height, &bw, &depth); *geometry = r; } /* }}} */ /** subSharedPropertyDelete {{{ * @brief Deletes the property * @param[in] disp Display * @param[in] win A #Window * @param[in] prop Property **/ void subSharedPropertyDelete(Display *disp, Window win, Atom prop) { assert(win); XDeleteProperty(disp, win, prop); } /* }}} */ /* Draw */ /** subSharedDrawString {{{ * @brief Draw text * @param[in] disp Display * @param[in] gc GC * @param[in] f A #SubFont * @param[in] win Target window * @param[in] x X position * @param[in] y Y position * @param[in] fg Foreground color * @param[in] bg Background color * @param[in] text Text to draw * @param[in] len Text length **/ void subSharedDrawString(Display *disp, GC gc, SubFont *f, Window win, int x, int y, long fg, long bg, const char *text, int len) { XGCValues gvals; assert(f && text); /* Draw text */ #ifdef HAVE_X11_XFT_XFT_H if(f->xft) ///< XFT { XftColor color = { 0 }; XColor xcolor = { 0 }; /* Get color values */ xcolor.pixel = fg; XQueryColor(disp, DefaultColormap(disp, DefaultScreen(disp)), &xcolor); color.pixel = xcolor.pixel; color.color.red = xcolor.red; color.color.green = xcolor.green; color.color.blue = xcolor.blue; color.color.alpha = 0xffff; XftDrawChange(f->draw, win); XftDrawStringUtf8(f->draw, &color, f->xft, x, y, (XftChar8 *)text, len); } else ///< XFS #endif /* HAVE_X11_XFT_XFT_H */ { /* Draw text */ gvals.foreground = fg; gvals.background = bg; XChangeGC(disp, gc, GCForeground|GCBackground, &gvals); XmbDrawString(disp, win, f->xfs, gc, x, y, text, len); } } /* }}} */ /** subSharedDrawIcon {{{ * @brief Draw text * @param[in] disp Display * @param[in] gc GC * @param[in] win Target window * @param[in] x X position * @param[in] y Y position * @param[in] width Width of pixmap * @param[in] height Height of pixmap * @param[in] fg Foreground color * @param[in] bg Background color * @param[in] pixmap Pixmap to draw * @param[in] bitmap Whether icon is a bitmap **/ void subSharedDrawIcon(Display *disp, GC gc, Window win, int x, int y, int width, int height, long fg, long bg, Pixmap pixmap, int bitmap) { XGCValues gvals; /* Plane color */ gvals.foreground = fg; gvals.background = bg; XChangeGC(disp, gc, GCForeground|GCBackground, &gvals); /* Copy icon to destination window */ if(bitmap) XCopyPlane(disp, pixmap, win, gc, 0, 0, width, height, x, y, 1); #ifdef HAVE_X11_XPM_H else XCopyArea(disp, pixmap, win, gc, 0, 0, width, height, x, y); #endif /* HAVE_X11_XPM_H */ } /* }}} */ /* Font */ /** subSharedFontNew {{{ * @brief Create new font * @param[in] disp Display * @param[in] name Font name * @return New #SubFont **/ SubFont * subSharedFontNew(Display *disp, const char *name) { SubFont *f = NULL; /* Load font */ #ifdef HAVE_X11_XFT_XFT_H if(!strncmp(name, "xft:", 4)) { XftFont *xft = NULL; /* Load XFT font */ if((xft = XftFontOpenName(disp, DefaultScreen(disp), name + 4))) { /* Create new font */ f = FONT(subSharedMemoryAlloc(1, sizeof(SubFont))); f->xft = xft; f->draw = XftDrawCreate(disp, DefaultRootWindow(disp), DefaultVisual(disp, DefaultScreen(disp)), DefaultColormap(disp, DefaultScreen(disp))); /* Font metrics */ f->height = f->xft->ascent + f->xft->descent + 2; f->y = (f->height - 2 + f->xft->ascent) / 2; } } else #endif /* HAVE_X11_XFT_XFT_H */ { int n = 0; char *def = NULL, **missing = NULL, **names = NULL; XFontStruct **xfonts = NULL; XFontSet xfs; /* Load font set */ if((xfs = XCreateFontSet(disp, name, &missing, &n, &def))) { /* Create new font */ f = FONT(subSharedMemoryAlloc(1, sizeof(SubFont))); f->xfs = xfs; XFontsOfFontSet(f->xfs, &xfonts, &names); /* Font metrics */ f->height = xfonts[0]->max_bounds.ascent + xfonts[0]->max_bounds.descent + 2; f->y = (f->height - 2 + xfonts[0]->max_bounds.ascent) / 2; if(missing) XFreeStringList(missing); ///< Ignore this } } return f; } /* }}} */ /** subSharedFontKill {{{ * @brief Kill a font * @param[in] disp Display * @param[in] f A #SubFont **/ void subSharedFontKill(Display *disp, SubFont *f) { assert(f); #ifdef HAVE_X11_XFT_XFT_H if(f->xft) { XftFontClose(disp, f->xft); XftDrawDestroy(f->draw); } else #endif /* HAVE_X11_XFT_XFT_H */ { XFreeFontSet(disp, f->xfs); } free(f); } /* }}} */ /* Misc */ /** subSharedParseColor {{{ * @brief Parse and load color * @param[in] disp Display * @param[in] name Color string * @return Color pixel value **/ unsigned long subSharedParseColor(Display *disp, char *name) { XColor xcolor = { 0 }; ///< Default color assert(name); /* Parse and store color */ if(!XParseColor(disp, DefaultColormap(disp, DefaultScreen(disp)), name, &xcolor)) { fprintf(stderr, " Failed loading color `%s'\n", name); } else if(!XAllocColor(disp, DefaultColormap(disp, DefaultScreen(disp)), &xcolor)) fprintf(stderr, " Failed allocating color `%s'\n", name); return xcolor.pixel; } /* }}} */ /** subSharedParseKey {{{ * @brief Parse key * @param[in] disp Display * @param[in] key Key string * @param[inout] code Key code * @param[inout] state Key state * @param[inout] mouse Whether this is mouse press * @return Color keysym or \p NoSymbol **/ KeySym subSharedParseKey(Display *disp, const char *key, unsigned int *code, unsigned int *state, int *mouse) { KeySym sym = NoSymbol; char *tokens = NULL, *tok = NULL, *save = NULL; /* Parse keys */ tokens = strdup(key); tok = strtok_r(tokens, "-", &save); while(tok) { /* Eval keysyms */ switch((sym = XStringToKeysym(tok))) { /* Unknown keys */ case NoSymbol: if('B' == tok[0]) { int buttonid = 0; sscanf(tok, "B%d", &buttonid); *mouse = True; *code = XK_Pointer_Button1 + buttonid; ///< FIXME: Implementation independent? sym = XK_Pointer_Button1; } else { free(tokens); return sym; } break; /* Modifier keys */ case XK_S: *state |= ShiftMask; break; case XK_C: *state |= ControlMask; break; case XK_A: *state |= Mod1Mask; break; case XK_M: *state |= Mod3Mask; break; case XK_W: *state |= Mod4Mask; break; case XK_G: *state |= Mod5Mask; break; /* Normal keys */ default: *mouse = False; *code = XKeysymToKeycode(disp, sym); } tok = strtok_r(NULL, "-", &save); } free(tokens); return sym; } /* }}} */ /** subSharedSpawn {{{ * @brief Spawn a command * @param[in] cmd Command string **/ pid_t subSharedSpawn(char *cmd) { pid_t pid = fork(); /* Handle fork pids */ switch(pid) { case 0: setsid(); execlp("/bin/sh", "sh", "-c", cmd, NULL); /* Never to be reached */ fprintf(stderr, " Failed executing command `%s'\n", cmd); exit(1); case -1: fprintf(stderr, " Failed forking command `%s'\n", cmd); } return pid; } /* }}} */ /** subSharedStringWidth {{{ * @brief Get width of the smallest enclosing box * @param[in] disp Display * @param[in] text The text * @param[in] len Length of the string * @param[inout] left Left bearing * @param[inout] right Right bearing * @param[in] center Center text * @return Width of the box **/ int subSharedStringWidth(Display *disp, SubFont *f, const char *text, int len, int *left, int *right, int center) { int width = 0, lbearing = 0, rbearing = 0; assert(f); /* Get text extents based on font */ if(text && 0 < len) { #ifdef HAVE_X11_XFT_XFT_H if(f->xft) ///< XFT { XGlyphInfo extents; XftTextExtentsUtf8(disp, f->xft, (XftChar8 *)text, len, &extents); width = extents.xOff; lbearing = extents.x; } else ///< XFS #endif /* HAVE_X11_XFT_XFT_H */ { XRectangle overall_ink = { 0 }, overall_logical = { 0 }; XmbTextExtents(f->xfs, text, len, &overall_ink, &overall_logical); width = overall_logical.width; lbearing = overall_logical.x; } /* Get left and right spacing */ if(left) *left = lbearing; if(right) *right = rbearing; } return center ? width - abs(lbearing - rbearing) : width; } /* }}} */ #ifndef SUBTLE /** subSharedMessage {{{ * @brief Send client message to window * @param[in] disp Display * @param[in] win Client window * @param[in] type Message type * @param[in] data A #SubMessageData * @param[in] format Data format * @param[in] xsync Sync connection * @returns **/ int subSharedMessage(Display *disp, Window win, char *type, SubMessageData data, int format, int xsync) { int status = 0; XEvent ev; long mask = SubstructureRedirectMask|SubstructureNotifyMask; assert(disp && win); /* Assemble event */ ev.xclient.type = ClientMessage; ev.xclient.serial = 0; ev.xclient.send_event = True; ev.xclient.message_type = XInternAtom(disp, type, False); ev.xclient.window = win; ev.xclient.format = format; /* Copy data over */ ev.xclient.data.l[0] = data.l[0]; ev.xclient.data.l[1] = data.l[1]; ev.xclient.data.l[2] = data.l[2]; ev.xclient.data.l[3] = data.l[3]; ev.xclient.data.l[4] = data.l[4]; status = XSendEvent(disp, DefaultRootWindow(disp), False, mask, &ev); if(True == xsync) XSync(disp, False); return status; } /* }}} */ #endif /* SUBTLE */ // vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/COPYING0000644000175000017500000004313411770332063015252 0ustar formorerformorer GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. subtle-0.11.3224-xi/test/0000755000175000017500000000000011770527221015173 5ustar formorerformorersubtle-0.11.3224-xi/test/icon/0000755000175000017500000000000011770527221016123 5ustar formorerformorersubtle-0.11.3224-xi/test/icon/clock.xbm0000644000175000017500000000020611770332063017722 0ustar formorerformorer#define clock_width 8 #define clock_height 8 static unsigned char clock_bits[] = { 0x3C, 0x5E, 0xEF, 0xF7, 0x87, 0xFF, 0x7E, 0x3C }; subtle-0.11.3224-xi/test/sublet/0000755000175000017500000000000011770527221016471 5ustar formorerformorersubtle-0.11.3224-xi/test/sublet/dummy.rb0000644000175000017500000000005611770332063020150 0ustar formorerformorerconfigure :dummy do |s| s.interval = 60 end subtle-0.11.3224-xi/test/contexts/0000755000175000017500000000000011770527221017042 5ustar formorerformorersubtle-0.11.3224-xi/test/contexts/view.rb0000644000175000017500000000560511770332063020345 0ustar formorerformorer# # @package test # # @file Test Subtlext::View functions # @author Christoph Kappel # @version $Id: test/contexts/view.rb,v 3169 2012/01/03 20:43:30 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'View' do VIEW_COUNT = 4 VIEW_ID = 0 VIEW_NAME = 'terms' VIEW_TAG = :default setup do # {{{ Subtlext::View.first(VIEW_ID) end # }}} asserts 'Check attributes' do # {{{ VIEW_ID == topic.id and VIEW_NAME == topic.name end # }}} asserts 'Get list' do # {{{ list = Subtlext::View.list list.is_a?(Array) and VIEW_COUNT == list.size and Subtlext::View.method(:all) == Subtlext::View.method(:list) end # }}} asserts 'Finder' do # {{{ index = Subtlext::View[VIEW_ID] string = Subtlext::View[VIEW_NAME] sym = Subtlext::View[VIEW_NAME.to_sym] all = Subtlext::View.find('.*') none = Subtlext::View['abcdef'] index == string and index == sym and all.is_a?(Array) and VIEW_COUNT == all.size and none.nil? end # }}} asserts 'First' do # {{{ index = Subtlext::View.first(VIEW_ID) string = Subtlext::View.first(VIEW_NAME) index == string end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(Subtlext::View.first(VIEW_ID)) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Check associations' do # {{{ clients = topic.clients tags = topic.tags clients.is_a?(Array) and 1 == clients.size and tags.is_a?(Array) and 2 == tags.size end # }}} asserts 'Check icon' do # {{{ nil == topic.icon end # }}} asserts 'Check current' do # {{{ topic.current? end # }}} asserts 'Convert to string' do # {{{ VIEW_NAME == topic.to_str end # }}} asserts 'Create new view' do # {{{ v = Subtlext::View.new('test') v.save sleep 1 5 == Subtlext::View.all.size end # }}} asserts 'Switch views' do # {{{ view_next = topic.next view_next.jump sleep 1 view_prev = view_next.prev topic.jump sleep 1 view_prev == topic end # }}} asserts 'Add/remove tags' do # {{{ tag = Subtlext::Tag.all.last # Compare tag counts before = topic.tags.size topic.tag(tag) sleep 0.5 middle1 = topic.tags.size topic.tags = [ tag, 'default' ] sleep 0.5 middle2 = topic.tags.size topic.untag(tag) sleep 0.5 after = topic.tags.size before == middle1 - 1 and 2 == middle2 and 1 == after and topic.has_tag?(VIEW_TAG) end # }}} asserts 'Store values' do # {{{ topic[:test] = 'test' 'test' == Subtlext::View.current[:test] end # }}} asserts 'Kill a view' do # {{{ Subtlext::View.first('test').kill sleep 1 VIEW_COUNT == Subtlext::View.all.size end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/subtle_init.rb0000644000175000017500000000170611770332063021712 0ustar formorerformorer# # @package test # # @file Test Subtlext::Subtle functions # @author Christoph Kappel # @version $Id: test/contexts/subtle_init.rb,v 3131 2011/11/15 20:43:06 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Subtle - Init' do COLORS_COUNT = 48 asserts 'Check running' do # {{{ Subtlext::Subtle.running? end # }}} asserts 'Check colors' do # {{{ Subtlext::Subtle.colors.is_a?(Hash) and COLORS_COUNT == Subtlext::Subtle.colors.size end # }}} asserts 'Check font' do # {{{ '-*-*-*-*-*-*-14-*-*-*-*-*-*-*' == Subtlext::Subtle.font end # }}} asserts 'Check spawn' do # {{{ if (xterm = find_executable0('xterm')).nil? raise 'xterm not found in path' else Subtlext::Subtle.spawn("#{xterm} -display :10") end sleep 1 1 == Subtlext::Client.all.size end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/tag.rb0000644000175000017500000000374211770332063020146 0ustar formorerformorer# # @package test # # @file Test Subtlext::Tag functions # @author Christoph Kappel # @version $Id: test/contexts/tag.rb,v 3169 2012/01/03 20:43:30 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Tag' do TAG_COUNT = 13 TAG_ID = 1 TAG_NAME = 'terms' setup do # {{{ Subtlext::Tag.first(TAG_ID) end # }}} asserts 'Check attributes' do # {{{ TAG_ID == topic.id and TAG_NAME == topic.name end # }}} asserts 'Get list' do # {{{ list = Subtlext::Tag.list list.is_a?(Array) and TAG_COUNT == list.size and Subtlext::Tag.method(:all) == Subtlext::Tag.method(:list) end # }}} asserts 'Finder' do # {{{ index = Subtlext::Tag[TAG_ID] string = Subtlext::Tag[TAG_NAME] sym = Subtlext::Tag[TAG_NAME.to_sym] all = Subtlext::Tag.find('.*') none = Subtlext::Tag['abcdef'] index == string and index == sym and all.is_a?(Array) and TAG_COUNT == all.size and none.nil? end # }}} asserts 'First' do # {{{ index = Subtlext::Tag.first(TAG_ID) string = Subtlext::Tag.first(TAG_NAME) index == string end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(Subtlext::Tag.first(TAG_ID)) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Check associations' do # {{{ clients = topic.clients views = topic.views clients.is_a?(Array) and 1 == clients.size and views.is_a?(Array) and 1 == views.size end # }}} asserts 'Convert to string' do # {{{ TAG_NAME == topic.to_str end # }}} asserts 'Create new tag' do # {{{ t = Subtlext::Tag.new('test') t.save sleep 1 TAG_COUNT + 1 == Subtlext::Tag.all.size end # }}} asserts 'Kill a tag' do # {{{ Subtlext::Tag.first('test').kill sleep 1 TAG_COUNT == Subtlext::Tag.all.size end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/sublet.rb0000644000175000017500000000323411770332063020665 0ustar formorerformorer# # @package test # # @file Test Subtlext::Sublet functions # @author Christoph Kappel # @version $Id: test/contexts/sublet.rb,v 3171 2012/01/03 20:53:09 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Sublet' do SUBLET_COUNT = 1 SUBLET_ID = 0 SUBLET_NAME = 'dummy' setup do # {{{ Subtlext::Sublet.first(SUBLET_ID) end # }}} asserts 'Check attributes' do # {{{ SUBLET_ID == topic.id and SUBLET_NAME == topic.name end # }}} asserts 'Get list' do # {{{ list = Subtlext::Sublet.list list.is_a?(Array) and SUBLET_COUNT == list.size and Subtlext::Sublet.method(:all) == Subtlext::Sublet.method(:list) end # }}} asserts 'Finder' do # {{{ index = Subtlext::Sublet[SUBLET_ID] string = Subtlext::Sublet[SUBLET_NAME] sym = Subtlext::Sublet[SUBLET_NAME.to_sym] all = Subtlext::Sublet.find('.*') none = Subtlext::Sublet['abcdef'] index == string and index == sym and none.nil? end # }}} asserts 'First' do # {{{ index = Subtlext::Sublet.first(SUBLET_ID) string = Subtlext::Sublet.first(SUBLET_NAME) index == string end # }}} asserts 'Update sublet' do # {{{ topic.update true end # }}} asserts 'Get geometry' do # {{{ topic.geometry.is_a?(Subtlext::Geometry) end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(topic) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Convert to string' do # {{{ SUBLET_NAME == topic.to_str end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/client.rb0000644000175000017500000000750511770332063020652 0ustar formorerformorer# # @package test # # @file Test Subtlext::Client functions # @author Christoph Kappel # @version $Id: test/contexts/client.rb,v 3169 2012/01/03 20:43:30 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Client' do CLIENT_COUNT = 1 CLIENT_ID = 0 CLIENT_NAME = 'xterm' CLIENT_TAG = :default setup do # {{{ Subtlext::Client.current end # }}} asserts 'Check attributes' do # {{{ CLIENT_NAME == topic.name and CLIENT_NAME == topic.instance and CLIENT_NAME == topic.klass.downcase end # }}} asserts 'Get list' do # {{{ list = Subtlext::Client.list list.is_a?(Array) and CLIENT_COUNT == list.size and Subtlext::Client.method(:all) == Subtlext::Client.method(:list) end # }}} asserts 'Finder' do # {{{ index = Subtlext::Client[CLIENT_ID] string = Subtlext::Client[CLIENT_NAME] sym = Subtlext::Client[CLIENT_NAME.to_sym] all = Subtlext::Client.find('.*') none = Subtlext::Client['abcdef'] index == string and index == sym and none.nil? end # }}} asserts 'First' do # {{{ index = Subtlext::Client.first(CLIENT_ID) string = Subtlext::Client.first(CLIENT_NAME) index == string end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(Subtlext::Client.current) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Convert to string' do # {{{ CLIENT_NAME == topic.to_str end # }}} asserts 'Send button' do # {{{ nil == topic.send_button(1) end # }}} asserts 'Send key' do # {{{ nil == topic.send_key('a') end # }}} asserts 'Add/remove flags' do # {{{ p = lambda { |c, is, toggle| ret = [] 2.times do |i| ret << c.send(is) c.send toggle sleep 0.5 end ret << c.send(is) ret } # Check flags expected = [ false, true, false ] results = [ :full ,:float, :stick, :resize, :urgent, :zaphod, :fixed, :borderless ].map { |flag| p.call(topic, "is_#{flag}?".to_sym, "toggle_#{flag}".to_sym) } results.all? { |r| r == expected } end # }}} asserts 'Add/remove tags' do # {{{ tag = Subtlext::Tag.all.last # Compare tag counts before = topic.tags.size topic.tag tag sleep 0.5 # Compare tag counts middle1 = topic.tags.size topic.tags = [ tag, 'default' ] sleep 0.5 # Compare tag counts middle2 = topic.tags.size topic.untag tag sleep 0.5 # Compare tag counts after = topic.tags.size before == middle1 - 1 and 2 == middle2 and before == after and topic.has_tag?(CLIENT_TAG) end # }}} asserts 'Set/get gravity' do # {{{ topic.gravity = 12 sleep 0.5 index = topic.gravity == Subtlext::Gravity.first(12) topic.gravity = :center sleep 0.5 sym = topic.gravity == Subtlext::Gravity.first(:center) topic.gravity = 'center' sleep 0.5 string = topic.gravity == Subtlext::Gravity.first('center') topic.gravity = Subtlext::Gravity.first(:center) sleep 0.5 string = topic.gravity == Subtlext::Gravity.first(:center) # Set gravity on www view topic.toggle_stick topic.gravity = { :www => :left } sleep 0.5 # Jump to www vew and check gravity Subtlext::View.first(:www).jump left = topic.gravity == Subtlext::Gravity.first(:left) index and sym and string and left end # }}} asserts 'Get Screen' do # {{{ topic.screen.is_a?(Subtlext::Screen) end # }}} asserts 'Store values' do # {{{ topic[:test] = 'test' 'test' == Subtlext::Client.current[:test] end # }}} asserts 'Kill a client' do # {{{ topic.kill sleep 1 0 == Subtlext::Client.all.size end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/geometry.rb0000644000175000017500000000237011770332063021222 0ustar formorerformorer# # @package test # # @file Test Subtlext::Geometry functions # @author Christoph Kappel # @version $Id: test/contexts/geometry.rb,v 3169 2012/01/03 20:43:30 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Geometry' do setup do # {{{ Subtlext::Geometry.new(0, 0, 1, 1) end # }}} asserts 'Init types' do # {{{ g1 = Subtlext::Geometry.new(topic) g2 = Subtlext::Geometry.new([ 0, 0, 1, 1 ]) g3 = Subtlext::Geometry.new({ x: 0, y: 0, width: 1, height: 1 }) g4 = Subtlext::Geometry.new("0x0+1+1") topic == g1 and g1 == g2 and g1 == g3 and g1 == g4 end # }}} asserts 'Check attributes' do # {{{ 0 == topic.x and 0 == topic.y and 1 == topic.width and 1 == topic.height end # }}} asserts 'Type conversions' do # {{{ hash = topic.to_hash ary = topic.to_ary hash.values == ary end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(Subtlext::Geometry.new(0, 0, 1, 1)) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Convert to string' do # {{{ '0x0+1+1' == topic.to_str end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/screen.rb0000644000175000017500000000303111770332063020641 0ustar formorerformorer# # @package test # # @file Test Subtlext::Screen functions # @author Christoph Kappel # @version $Id: test/contexts/screen.rb,v 3169 2012/01/03 20:43:30 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Screen' do setup do # {{{ Subtlext::Screen.current end # }}} asserts 'Check attributes' do # {{{ 0 == topic.id and '0x16+1024+752' == topic.geometry.to_str end # }}} asserts 'Get list' do # {{{ list = Subtlext::Screen.list list.is_a?(Array) and 1 == list.size and Subtlext::Screen.method(:all) == Subtlext::Screen.method(:list) end # }}} asserts 'Find and compare' do # {{{ topic == Subtlext::Screen[0] end # }}} asserts 'Finder' do # {{{ Subtlext::Screen[0] == Subtlext::Screen.find( Subtlext::Geometry.new(100, 100, 100, 100) ) end # }}} asserts 'Check current' do # {{{ topic.current? end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(Subtlext::Screen.current) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Change view' do # {{{ view1 = topic.view sleep 0.5 topic.view = 'www' sleep 0.5 view2 = topic.view topic.view = view1 sleep 0.5 view3 = topic.view view1 == view3 and 'www' == view2.name end # }}} asserts 'Convert to string' do # {{{ '0x16+1024+752' == topic.to_str end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/color.rb0000644000175000017500000000230611770332063020504 0ustar formorerformorer# # @package test # # @file Test Subtlext::Color functions # @author Christoph Kappel # @version $Id: test/contexts/color.rb,v 3169 2012/01/03 20:43:30 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Color' do setup do # {{{ Subtlext::Color.new '#ff0000' end # }}} asserts 'Init types' do # {{{ c1 = Subtlext::Color.new(topic) c2 = Subtlext::Color.new([ 255, 0, 0 ]) c3 = Subtlext::Color.new({ red: 255, green: 0, blue: 0 }) topic == c1 and c1 == c2 and c1 == c3 end # }}} asserts 'Check attributes' do # {{{ 255 == topic.red and 0 == topic.green and 0 == topic.blue end # }}} asserts 'Type conversions' do # {{{ hex = topic.to_hex hash = topic.to_hash ary = topic.to_ary '#FF0000' == hex and hash.values == ary end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(Subtlext::Color.new('#ff0000')) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Convert to string' do # {{{ topic.to_str.match(/<>#[0-9]+<>/) end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/gravity.rb0000644000175000017500000000464511770332063021063 0ustar formorerformorer# # @package test # # @file Test Subtlext::Gravity functions # @author Christoph Kappel # @version $Id: test/contexts/gravity.rb,v 3169 2012/01/03 20:43:30 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Gravity' do GRAVITY_COUNT = 30 GRAVITY_ID = 12 GRAVITY_NAME = 'center' setup do # {{{ Subtlext::Gravity.first(GRAVITY_ID) end # }}} asserts 'Check attributes' do # {{{ GRAVITY_ID == topic.id and GRAVITY_NAME == topic.name and topic.geometry.is_a? Subtlext::Geometry end # }}} asserts 'Get list' do # {{{ list = Subtlext::Gravity.list list.is_a?(Array) and GRAVITY_COUNT == list.size and Subtlext::Gravity.method(:all) == Subtlext::Gravity.method(:list) end # }}} asserts 'Finder' do # {{{ index = Subtlext::Gravity[GRAVITY_ID] string = Subtlext::Gravity[GRAVITY_NAME + '$'] sym = Subtlext::Gravity[GRAVITY_NAME.to_sym] all = Subtlext::Gravity.find('.*') none = Subtlext::Gravity['abcdef'] index == string and index == sym and all.is_a?(Array) and GRAVITY_COUNT == all.size and none.nil? end # }}} asserts 'First' do # {{{ index = Subtlext::Gravity.first(GRAVITY_ID) string = Subtlext::Gravity.first(GRAVITY_NAME) index == string end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(Subtlext::Gravity.first(GRAVITY_ID)) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Check associations' do # {{{ clients = topic.clients clients.is_a?(Array) and 1 == clients.size end # }}} asserts 'Check geometry for screen' do # {{{ screen = Subtlext::Screen.current geom = topic.geometry_for(screen) screen.geometry == geom end # }}} asserts 'Convert to string' do # {{{ GRAVITY_NAME == topic.to_str end # }}} asserts 'Create new gravity' do # {{{ g = Subtlext::Gravity.new('test', 0, 0, 100, 100) g.save sleep 0.5 GRAVITY_COUNT + 1 == Subtlext::Gravity.all.size end # }}} asserts 'Test tiling' do # {{{ topic.tiling = :vert topic.tiling = :horz topic.tiling = nil true end # }}} asserts 'Kill a gravity' do # {{{ Subtlext::Gravity.first('test').kill sleep 0.5 GRAVITY_COUNT == Subtlext::Gravity.all.size end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/subtle_finish.rb0000644000175000017500000000143311770332063022224 0ustar formorerformorer# # @package test # # @file Test Subtlext::Subtle functions # @author Christoph Kappel # @version $Id: test/contexts/subtle_finish.rb,v 3074 2011/10/07 19:26:51 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Subtle - Finish' do asserts 'Check reload' do # {{{ Subtlext::Subtle.reload Subtlext::Subtle.running? end # }}} asserts 'Check restart' do # {{{ Subtlext::Subtle.reload Subtlext::Subtle.running? end # }}} asserts 'Check quit' do # {{{ # Kill all clients Subtlext::Client.all.each do |c| c.kill end sleep 0.5 Subtlext::Subtle.quit sleep 1 !Subtlext::Subtle.running? end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/tray.rb0000644000175000017500000000335111770332063020346 0ustar formorerformorer# # @package test # # @file Test Subtlext::Tray functions # @author Christoph Kappel # @version $Id: test/contexts/tray.rb,v 3169 2012/01/03 20:43:30 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Tray' do TRAY_ID = 0 TRAY_COUNT = 1 TRAY_NAME = 'test.rb' setup do # {{{ Subtlext::Tray.first(TRAY_ID) end # }}} asserts 'Check attributes' do # {{{ TRAY_NAME == topic.name and TRAY_NAME == topic.instance and TRAY_NAME == topic.klass.downcase end # }}} asserts 'Get list' do # {{{ list = Subtlext::Tray.list list.is_a?(Array) and TRAY_COUNT == list.size and Subtlext::Tray.method(:all) == Subtlext::Tray.method(:list) end # }}} asserts 'Finder' do # {{{ index = Subtlext::Tray[TRAY_ID] string = Subtlext::Tray[TRAY_NAME] sym = Subtlext::Tray[TRAY_NAME.to_sym] all = Subtlext::Tray.find('.*') none = Subtlext::Tray['abcdef'] index == string and index == sym and none.nil? end # }}} asserts 'First' do # {{{ index = Subtlext::Tray.first(TRAY_ID) string = Subtlext::Tray.first(TRAY_NAME) index == string end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(Subtlext::Tray.first(TRAY_ID)) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Convert to string' do # {{{ TRAY_NAME == topic.to_str end # }}} asserts 'Send button' do # {{{ nil == topic.send_button(1) end # }}} asserts 'Send key' do # {{{ nil == topic.send_key('a') end # }}} asserts 'Kill a tray' do # {{{ nil == topic.kill end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/contexts/icon.rb0000644000175000017500000000210111770332063020307 0ustar formorerformorer# # @package test # # @file Test Subtlext::Icon functions # @author Christoph Kappel # @version $Id: test/contexts/icon.rb,v 3169 2012/01/03 20:43:30 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # context 'Icon' do setup do # {{{ Subtlext::Icon.new('icon/clock.xbm') end # }}} asserts 'Equal and compare' do # {{{ topic.eql?(Subtlext::Icon.new(8, 8)) and topic == topic end # }}} asserts 'Hash and unique' do # {{{ 1 == [ topic, topic ].uniq.size end # }}} asserts 'Check attributes' do # {{{ 8 == topic.height and 8 == topic.width end # }}} asserts 'Draw routines' do # {{{ topic.draw_point(1, 1) topic.draw_rect(1, 1, 6, 6, false) topic.clear true end # }}} asserts 'Copy area' do # {{{ icon = Subtlext::Icon.new('icon/clock.xbm') topic.copy_area(icon, 0, 0, 4, 4, 2, 2) true end # }}} asserts 'Convert to string' do # {{{ topic.to_str.match(/<>![0-9]+<>/) end # }}} end # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/test.rb0000644000175000017500000000320211770332063016472 0ustar formorerformorer#!/usr/bin/ruby # # @package test # # @file Run riot unit tests # @author Christoph Kappel # @version $Id: test/test.rb,v 3131 2011/11/15 20:43:06 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # # Configuration subtle = "../subtle" subtlext = "../subtlext.so" config = "../data/subtle.rb" sublets = "./sublet" display = ":10" begin require "mkmf" require "riot" require "gtk2/base" require subtlext rescue LoadError => missing puts <>> ERROR: Couldn't find the gem `#{missing}' >>> Please install it with following command: >>> gem install #{missing} EOF end def fork_and_forget(cmd) pid = Process.fork if pid.nil? exec(cmd) else Process.detach(pid) end end # Find xterm if (xterm = find_executable0("xterm")).nil? raise "xterm not found in path" end # Start subtle fork_and_forget("#{subtle} -d #{display} -c #{config} -s #{sublets} &>/dev/null") sleep 1 # Create dummy tray icon Gtk.init(["--display=%s" % [ display ]]) Gtk::StatusIcon.new # Set subtlext display Subtlext::Subtle.display = display # Run tests require_relative "contexts/subtle_init.rb" require_relative "contexts/color.rb" require_relative "contexts/geometry.rb" require_relative "contexts/gravity.rb" require_relative "contexts/icon.rb" require_relative "contexts/screen.rb" require_relative "contexts/sublet.rb" require_relative "contexts/tag.rb" require_relative "contexts/view.rb" require_relative "contexts/client.rb" require_relative "contexts/tray.rb" require_relative "contexts/subtle_finish.rb" # vim:ts=2:bs=2:sw=2:et:fdm=marker subtle-0.11.3224-xi/test/xserver.rb0000644000175000017500000000075711770332063017225 0ustar formorerformorer#!/usr/bin/ruby # # @package test # # @file Start Xvfb server # @author Christoph Kappel # @version $Id: test/xserver.rb,v 3074 2011/10/07 19:26:51 unexist $ # # This program can be distributed under the terms of the GNU GPLv2. # See the file COPYING for details. # require "mkmf" # Find Xvfb if((xvfb = find_executable0("Xvfb")).nil?) raise "Xvfb not found in path" end system("#{xvfb} :10 -screen 0 1024x768x16 -I &> /dev/null") # vim:ts=2:bs=2:sw=2:et:fdm=marker