func CreateRectangleFromString(spec string) (*Rectangle, error) {
	coords := strings.SplitN(spec, `,`, 4)

	if len(coords) == 4 {
		rect := &Rectangle{}

		if x0, err := stringutil.ConvertToInteger(coords[0]); err == nil {
			rect.TopLeft.X = int(x0)
		} else {
			return nil, fmt.Errorf("Invalid x0 coordinate: %v", err)
		}

		if y0, err := stringutil.ConvertToInteger(coords[1]); err == nil {
			rect.TopLeft.Y = int(y0)
		} else {
			return nil, fmt.Errorf("Invalid y0 coordinate: %v", err)
		}

		if x1, err := stringutil.ConvertToInteger(coords[2]); err == nil {
			rect.BottomRight.X = int(x1)
		} else {
			return nil, fmt.Errorf("Invalid x1 coordinate: %v", err)
		}

		if y1, err := stringutil.ConvertToInteger(coords[3]); err == nil {
			rect.BottomRight.Y = int(y1)
		} else {
			return nil, fmt.Errorf("Invalid y1 coordinate: %v", err)
		}

		return rect, nil
	}

	return nil, fmt.Errorf("Expected 4 values, got %d", len(coords))
}
func CreatePointsFromString(spec string) []Point {
	points := make([]Point, 0)

	pairs := strings.Split(spec, `|`)

	for _, pair := range pairs {
		p := strings.SplitN(pair, `,`, 2)

		if len(p) == 2 {
			if x, err := stringutil.ConvertToInteger(p[0]); err == nil {
				if y, err := stringutil.ConvertToInteger(p[1]); err == nil {
					points = append(points, Point{
						X: int(x),
						Y: int(y),
					})
				}
			}
		}
	}

	return points
}
func (self *Theme) generateThemeDefinition() error {
	for _, themeDir := range self.ThemeDirs {
		//  glob for all sub-subdirs in a candidate directory: <base>/<name>/*/*
		//      e.g.:  /usr/share/icons/hicolor/apps/16
		//
		candidateDir := path.Join(themeDir, self.InternalName)

		if files, err := filepath.Glob(path.Join(candidateDir, `*/*`)); err == nil {
			for _, filename := range files {
				//  verify we have a directory
				if stat, err := os.Stat(filename); err == nil && stat.IsDir() {
					themeSubdir := strings.TrimPrefix(strings.TrimPrefix(filename, candidateDir), `/`)
					self.Directories = append(self.Directories, themeSubdir)

					//  try to figure out the context of this subdirectory
					if parts := strings.SplitN(themeSubdir, `/`, 2); len(parts) == 2 {
						sizeParts := strings.Split(parts[1], `x`)
						context := IconContext{
							Subdirectory: themeSubdir,
							Threshold:    DEFAULT_ICON_CONTEXT_THRESHOLD,
						}

						if sizeParts[0] == `scalable` {
							context.Type = IconContextScalable
							context.Size = DEFAULT_ICON_CONTEXT_SCALABLE_SIZE
							context.MinSize = DEFAULT_ICON_CONTEXT_SCALABLE_MIN
							context.MaxSize = DEFAULT_ICON_CONTEXT_SCALABLE_MAX

						} else if v, err := stringutil.ConvertToInteger(sizeParts[0]); err == nil {
							context.Type = IconContextFixed
							context.Size = int(v)
						}

						self.Contexts[themeSubdir] = context
					}
				}
			}
		}
	}

	//  if we got to this point and still don't have any directories in our list,
	//  give up. we really, really tried....
	if len(self.Directories) == 0 {
		return fmt.Errorf("Unable to generate theme definition")
	}

	//  we're here! set spec defaults
	self.Inherits = []string{DEFAULT_ICONTHEME_INHERIT}

	return nil
}
func (self *SoundctlModule) getNamedOutput(backendName string, outputName string) (types.IOutput, error) {
	if backend, ok := self.Backends[backendName]; ok {
		outputs := backend.GetOutputs()

		if outputName == `current` {
			if output, err := backend.GetCurrentOutput(); err == nil {
				return output, nil
			} else {
				return output, err
			}
		} else if i, err := stringutil.ConvertToInteger(outputName); err == nil && int(i) < len(outputs) {
			return outputs[i], nil
		} else {
			if output, ok := backend.GetOutputByName(outputName); ok {
				return output, nil
			}
		}

		return nil, fmt.Errorf("Unable to locate output '%s' on backend '%s'", outputName, backendName)
	} else {
		return nil, fmt.Errorf("Unable to locate backend '%s'", backendName)
	}
}
func (self *Theme) refreshThemeDefinition() error {
	for _, themeDir := range self.ThemeDirs {
		themeIndexFilename := path.Join(themeDir, self.InternalName, self.IndexFile)

		if themeIndex, err := ini.LoadFile(themeIndexFilename); err == nil {
			if config, ok := themeIndex[`Icon Theme`]; ok {
				if v, ok := config[`Name`]; ok {
					self.Name = v
				}

				self.Inherits = strings.Split(config[`Inherits`], `,`)

				if len(self.Inherits) == 0 {
					self.Inherits = []string{DEFAULT_ICONTHEME_INHERIT}
				}

				self.Comment = config[`Comment`]
				self.Example = config[`Example`]

				if v, ok := config[`Hidden`]; ok {
					self.Hidden = (v == `true`)
				}

				if v, ok := config[`Directories`]; ok {
					self.Directories = strings.Split(v, `,`)

					for _, directory := range self.Directories {
						if contextConfig, ok := themeIndex[directory]; ok {
							context := IconContext{
								Subdirectory: directory,
							}

							if v, err := stringutil.ConvertToInteger(contextConfig[`Size`]); err == nil {
								context.Size = int(v)
							}

							if v, ok := contextConfig[`Context`]; ok {
								context.Name = v
							}

							context.MinSize = context.Size
							context.MaxSize = context.Size
							context.Threshold = DEFAULT_ICON_CONTEXT_THRESHOLD

							switch strings.ToLower(contextConfig[`Type`]) {
							case `fixed`:
								context.Type = IconContextFixed

								if minSize, ok := contextConfig[`MinSize`]; ok {
									if v, err := stringutil.ConvertToInteger(minSize); err == nil {
										context.MinSize = int(v)
									}
								}

								if maxSize, ok := contextConfig[`MaxSize`]; ok {
									if v, err := stringutil.ConvertToInteger(maxSize); err == nil {
										context.MaxSize = int(v)
									}
								}

							case `scalable`:
								context.Type = IconContextScalable

							default:
								context.Type = IconContextThreshold

								if threshold, ok := contextConfig[`Threshold`]; ok {
									if v, err := stringutil.ConvertToInteger(threshold); err == nil {
										context.Threshold = int(v)
									}
								}
							}

							self.Contexts[directory] = context
						}
					}
				}

			} else {
				return fmt.Errorf("Cannot load theme at %s: missing [Icon Theme] section", themeIndexFilename)
			}

			self.loadedDef = true
			break
		}
	}

	if !self.loadedDef {
		if err := self.generateThemeDefinition(); err != nil {
			return fmt.Errorf("Unable to find a theme definition file for '%s' in any directories", self.InternalName)
		}
	}

	return nil
}
func (self *SessionModule) LoadRoutes(router *httprouter.Router) error {
	router.GET(`/api/session/workspaces`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		if workspaces, err := self.GetAllWorkspaces(); err == nil {
			util.Respond(w, http.StatusOK, workspaces, nil)
		} else {
			util.Respond(w, http.StatusInternalServerError, nil, err)
		}
	})

	router.GET(`/api/session/workspaces/current`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		if workspaces, err := self.GetAllWorkspaces(); err == nil {
			for _, workspace := range workspaces {
				if workspace.IsCurrent {
					util.Respond(w, http.StatusOK, workspace, nil)
					return
				}
			}

			util.Respond(w, http.StatusNotFound, nil, fmt.Errorf("Current workspace not found"))
		} else {
			util.Respond(w, http.StatusInternalServerError, nil, err)
		}
	})

	router.GET(`/api/session/windows`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		if windows, err := self.GetAllWindows(); err == nil {
			for i, _ := range windows {
				windows[i].IconUri = fmt.Sprintf("/api/session/windows/%d/icon", windows[i].ID)
			}

			util.Respond(w, http.StatusOK, windows, nil)
		} else {
			util.Respond(w, http.StatusInternalServerError, nil, err)
		}
	})

	router.GET(`/api/session/windows/:id`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		if window, err := self.GetWindow(params.ByName(`id`)); err == nil {
			window.IconUri = fmt.Sprintf("/api/session/windows/%s/icon", params.ByName(`id`))

			util.Respond(w, http.StatusOK, window, nil)
		} else {
			util.Respond(w, http.StatusInternalServerError, nil, err)
		}
	})

	router.GET(`/api/session/windows/:id/icon`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		var buffer bytes.Buffer
		width := uint(16)
		height := uint(16)

		if w := req.URL.Query().Get(`w`); w != `` {
			if value, err := stringutil.ConvertToInteger(w); err == nil {
				width = uint(value)
			}
		}

		if h := req.URL.Query().Get(`h`); h != `` {
			if value, err := stringutil.ConvertToInteger(h); err == nil {
				height = uint(value)
			}
		}

		if height != width {
			height = width
		}

		if err := self.WriteWindowIcon(params.ByName(`id`), width, height, &buffer); err == nil {
			w.Header().Set(`Content-Type`, `image/png`)
			w.Write(buffer.Bytes())
			return
		} else {
			util.Respond(w, http.StatusInternalServerError, nil, err)
		}
	})

	router.GET(`/api/session/windows/:id/image`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		var buffer bytes.Buffer

		if err := self.WriteWindowImage(params.ByName(`id`), &buffer); err == nil {
			w.Header().Set(`Content-Type`, `image/png`)
			w.Write(buffer.Bytes())
			return
		} else {
			util.Respond(w, http.StatusInternalServerError, nil, err)
		}
	})

	router.PUT(`/api/session/windows/:id/do/:action`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		var err error
		id := params.ByName(`id`)

		switch params.ByName(`action`) {
		case `maximize`:
			err = self.MaximizeWindow(id)

		case `max-x`:
			err = self.MaximizeWindowHorizontal(id)

		case `max-y`:
			err = self.MaximizeWindowVertical(id)

		case `minimize`:
			err = self.MinimizeWindow(id)

		case `restore`:
			err = self.RestoreWindow(id)

		case `hide`:
			err = self.HideWindow(id)

		case `show`:
			err = self.ShowWindow(id)

		case `raise`:
			err = self.RaiseWindow(id)

		default:
			util.Respond(w, http.StatusBadRequest, nil, fmt.Errorf("Unknown action '%s'", params.ByName(`action`)))
			return
		}

		if err == nil {
			util.Respond(w, http.StatusAccepted, nil, nil)
		} else {
			util.Respond(w, http.StatusInternalServerError, nil, err)
		}
	})

	router.PUT(`/api/session/windows/:id/move/:x/:y`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		id := params.ByName(`id`)
		var x, y int

		if value, err := stringutil.ConvertToInteger(params.ByName(`x`)); err == nil {
			x = int(value)
		} else {
			util.Respond(w, http.StatusBadRequest, nil, err)
			return
		}

		if value, err := stringutil.ConvertToInteger(params.ByName(`y`)); err == nil {
			y = int(value)
		} else {
			util.Respond(w, http.StatusBadRequest, nil, err)
			return
		}

		if err := self.MoveWindow(id, x, y); err == nil {
			util.Respond(w, http.StatusAccepted, nil, nil)
		} else {
			util.Respond(w, http.StatusInternalServerError, nil, err)
		}
	})

	router.PUT(`/api/session/windows/:id/resize/:x/:y`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		id := params.ByName(`id`)
		var width, height uint

		if value, err := stringutil.ConvertToInteger(params.ByName(`width`)); err == nil {
			width = uint(value)
		} else {
			util.Respond(w, http.StatusBadRequest, nil, err)
			return
		}

		if value, err := stringutil.ConvertToInteger(params.ByName(`height`)); err == nil {
			height = uint(value)
		} else {
			util.Respond(w, http.StatusBadRequest, nil, err)
			return
		}

		if err := self.ResizeWindow(id, width, height); err == nil {
			util.Respond(w, http.StatusAccepted, nil, nil)
		} else {
			util.Respond(w, http.StatusInternalServerError, nil, err)
		}
	})

	router.GET(`/api/session/applications`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		keys := make([]string, 0)

		for key, _ := range self.Applications.Entries {
			keys = append(keys, key)
		}

		sort.Strings(keys)

		util.Respond(w, http.StatusOK, keys, nil)
	})

	router.GET(`/api/session/applications/:name`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		key := params.ByName(`name`)

		if app, ok := self.Applications.Entries[key]; ok {
			util.Respond(w, http.StatusOK, app, nil)
		} else {
			util.Respond(w, http.StatusNotFound, nil, fmt.Errorf("Could not locate application '%s'", key))
		}
	})

	router.GET(`/api/session/icons/list/:type`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		var filterMinSize, filterMaxSize int

		rv := make([]string, 0)
		listType := params.ByName(`type`)

		//  filters
		filterThemes := strings.Split(req.URL.Query().Get(`themes`), `,`)
		filterIconContextTypes := strings.Split(req.URL.Query().Get(`contexts`), `,`)
		filterIconFileTypes := strings.Split(req.URL.Query().Get(`filetypes`), `,`)
		filterMinSizeS := req.URL.Query().Get(`minsize`)
		filterMaxSizeS := req.URL.Query().Get(`maxsize`)
		filterIsScalable := req.URL.Query().Get(`scalable`)

		if filterMinSizeS != `` {
			if v, err := stringutil.ConvertToInteger(filterMinSizeS); err == nil {
				filterMinSize = int(v)
			} else {
				util.Respond(w, http.StatusBadRequest, nil, err)
				return
			}
		}

		if filterMaxSizeS != `` {
			if v, err := stringutil.ConvertToInteger(filterMaxSizeS); err == nil {
				filterMaxSize = int(v)
			} else {
				util.Respond(w, http.StatusBadRequest, nil, err)
				return
			}
		}

		for _, theme := range self.Themeset.Themes {
			if len(filterThemes) > 0 && filterThemes[0] != `` {
				if !sliceutil.ContainsString(filterThemes, strings.ToLower(theme.InternalName)) {
					inInherited := false

					for _, inheritedThemeName := range theme.Inherits {
						if sliceutil.ContainsString(filterThemes, strings.ToLower(inheritedThemeName)) {
							inInherited = true
							break
						}
					}

					if !inInherited {
						continue
					}
				}
			}

			switch listType {
			case `themes`:
				if !sliceutil.ContainsString(rv, theme.Name) {
					rv = append(rv, theme.InternalName)
				}
			default:
				for _, icon := range theme.Icons {
					//  filter context types
					if len(filterIconContextTypes) > 0 && filterIconContextTypes[0] != `` {
						if !sliceutil.ContainsString(filterIconContextTypes, strings.ToLower(icon.Context.Name)) {
							continue
						}
					}

					//  filter icon filetypes
					if len(filterIconFileTypes) > 0 && filterIconFileTypes[0] != `` {
						if !sliceutil.ContainsString(filterIconFileTypes, icon.Type) {
							continue
						}
					}

					//  filter icon size contraints
					if filterMinSize > 0 && icon.Context.MinSize < filterMinSize {
						continue
					}

					if filterMaxSize > 0 && icon.Context.MaxSize > filterMaxSize {
						continue
					}

					//  filter for scalable/non-scalable icons
					if filterIsScalable == `true` && icon.Context.Type != icons.IconContextScalable {
						continue
					} else if filterIsScalable == `false` && icon.Context.Type == icons.IconContextScalable {
						continue
					}

					var value string

					switch listType {
					case `names`:
						value = icon.Name
					case `contexts`:
						value = strings.ToLower(icon.Context.Name)
					case `display-names`:
						value = icon.DisplayName
					case `sizes`:
						if v, err := stringutil.ToString(icon.Context.Size); err == nil {
							value = v
						}
					default:
						util.Respond(w, http.StatusBadRequest, nil, fmt.Errorf("Unrecognized list type '%s'", listType))
						return
					}

					if value != `` {
						if !sliceutil.ContainsString(rv, value) {
							rv = append(rv, value)
						}
					}
				}
			}
		}

		sort.Strings(rv)

		util.Respond(w, http.StatusOK, rv, nil)
	})

	router.GET(`/api/session/icons/view/:name/size/:size`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		var iconSize int

		iconNames := strings.Split(params.ByName(`name`), `,`)
		iconSizeS := params.ByName(`size`)
		themeName := req.URL.Query().Get(`theme`)

		if themeName == `` {
			themeName = self.Themeset.DefaultTheme
		}

		if v, err := stringutil.ConvertToInteger(iconSizeS); err == nil {
			iconSize = int(v)

			var icon *icons.Icon

			switch req.URL.Query().Get(`mode`) {
			case `hicolor-first`:
				if hiColorIcon, ok := self.Themeset.FindIconViaTheme(`hicolor`, iconNames, iconSize); ok {
					icon = hiColorIcon
				} else if themeIcon, ok := self.Themeset.FindIconViaTheme(themeName, iconNames, iconSize); ok {
					icon = themeIcon
				}
			default:
				if themeIcon, ok := self.Themeset.FindIconViaTheme(themeName, iconNames, iconSize); ok {
					icon = themeIcon
				}
			}

			if icon != nil {
				var contentType string

				switch icon.Type {
				case `png`:
					contentType = `image/png`
				case `svg`:
					contentType = `image/svg+xml`
				default:
					util.Respond(w, http.StatusBadRequest, nil, fmt.Errorf("Unsupported icon type '%s'", icon.Type))
					return
				}

				defer icon.Close()

				if data, err := ioutil.ReadAll(icon); err == nil {
					w.Header().Set(`Content-Type`, contentType)
					w.Write(data)
				} else {
					util.Respond(w, http.StatusBadRequest, nil, err)
				}
			} else {
				util.Respond(w, http.StatusNotFound, nil, fmt.Errorf("Could not locate icon"))
			}
		} else {
			util.Respond(w, http.StatusBadRequest, nil, err)
		}
	})

	// router.GET(`/api/session/applications/find/:pattern`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	// })

	router.PUT(`/api/session/applications/:name/launch`, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		if err := self.Applications.LaunchEntry(params.ByName(`name`)); err == nil {
			util.Respond(w, http.StatusAccepted, nil, nil)
		} else {
			util.Respond(w, http.StatusNotFound, nil, err)
		}

	})

	return nil
}