os.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package main
  2. import (
  3. "os"
  4. "fmt"
  5. "slices"
  6. "syscall"
  7. "os/exec"
  8. "os/signal"
  9. "path/filepath"
  10. "strings"
  11. "github.com/gotk3/gotk3/gtk"
  12. )
  13. type desktopData struct {
  14. Name string
  15. Icon string
  16. Exec string
  17. }
  18. var desktopDirs = getAppDirs()
  19. func getDesktopData(className string) (desktopData, error) {
  20. allData, err := loadTextFile(searchDesktopFile(className))
  21. if err != nil {
  22. return desktopData{}, err
  23. }
  24. data := desktopData{
  25. Name: getDesktopOption(allData, "Name"),
  26. Icon: getDesktopOption(allData, "Icon"),
  27. Exec: getDesktopOption(allData, "Exec"),
  28. }
  29. return data, nil
  30. }
  31. func searchDesktopFile(className string) string{
  32. for _, appDir := range desktopDirs {
  33. desktopFile := className + ".desktop"
  34. _, err := os.Stat(filepath.Join(appDir, desktopFile))
  35. if err == nil {
  36. return filepath.Join(appDir, desktopFile)
  37. }
  38. // If file non found
  39. files, _ := os.ReadDir(appDir)
  40. for _, file := range files {
  41. fileName := file.Name()
  42. // "krita" > "org.kde.krita.desktop" / "lutris" > "net.lutris.Lutris.desktop"
  43. if strings.Count(fileName, ".") > 1 && strings.Contains(fileName, className) {
  44. return filepath.Join(appDir, fileName)
  45. }
  46. // "VirtualBox Manager" > "virtualbox.desktop"
  47. if fileName == strings.Split(strings.ToLower(className), " ")[0] + ".desktop" {
  48. return filepath.Join(appDir, fileName)
  49. }
  50. }
  51. }
  52. return ""
  53. }
  54. func getDesktopOption(allData []string, option string) string {
  55. for lineIndex := range len(allData) {
  56. line := allData[lineIndex]
  57. if strings.HasPrefix(line, option + "=") {
  58. optionValue := strings.Split(line, "=")[1]
  59. return optionValue
  60. }
  61. }
  62. return ""
  63. }
  64. func loadTextFile(path string) ([]string, error) {
  65. bytes, err := os.ReadFile(path)
  66. if err != nil {
  67. return nil, err
  68. }
  69. lines := strings.Split(string(bytes), "\n")
  70. var output []string
  71. for _, line := range lines {
  72. line = strings.TrimSpace(line)
  73. if line != "" {
  74. output = append(output, line)
  75. }
  76. }
  77. return output, nil
  78. }
  79. func launch(command string) {
  80. if strings.Contains(command, "\"") {
  81. command = strings.ReplaceAll(command, "\"", "")
  82. }
  83. badArg := strings.Index(command, "%")
  84. if badArg != -1 {
  85. command = command[:badArg-1]
  86. }
  87. elements := strings.Split(command, " ")
  88. // find prepended env variables, if any
  89. envVarsNum := strings.Count(command, "=")
  90. var envVars []string
  91. cmdIdx := -1
  92. if envVarsNum > 0 {
  93. for idx, item := range elements {
  94. if strings.Contains(item, "=") {
  95. envVars = append(envVars, item)
  96. } else if !strings.HasPrefix(item, "-") && cmdIdx == -1 {
  97. cmdIdx = idx
  98. }
  99. }
  100. }
  101. if cmdIdx == -1 {
  102. cmdIdx = 0
  103. }
  104. var args []string
  105. for _, arg := range elements[1+cmdIdx:] {
  106. if !strings.Contains(arg, "=") {
  107. args = append(args, arg)
  108. }
  109. }
  110. cmd := exec.Command(elements[cmdIdx], elements[1+cmdIdx:]...)
  111. // set env variables
  112. if len(envVars) > 0 {
  113. cmd.Env = os.Environ()
  114. cmd.Env = append(cmd.Env, envVars...)
  115. }
  116. msg := fmt.Sprintf("env vars: %s; command: '%s'; args: %s\n", envVars, elements[cmdIdx], args)
  117. fmt.Println(msg)
  118. if err := cmd.Start(); err != nil {
  119. fmt.Println("Unable to launch command!", err.Error())
  120. }
  121. }
  122. func getAppDirs() []string {
  123. var dirs []string
  124. xdgDataDirs := ""
  125. home := os.Getenv("HOME")
  126. xdgDataHome := os.Getenv("XDG_DATA_HOME")
  127. if os.Getenv("XDG_DATA_DIRS") != "" {
  128. xdgDataDirs = os.Getenv("XDG_DATA_DIRS")
  129. } else {
  130. xdgDataDirs = "/usr/local/share/:/usr/share/"
  131. }
  132. if xdgDataHome != "" {
  133. dirs = append(dirs, filepath.Join(xdgDataHome, "applications"))
  134. } else if home != "" {
  135. dirs = append(dirs, filepath.Join(home, ".local/share/applications"))
  136. }
  137. for _, d := range strings.Split(xdgDataDirs, ":") {
  138. dirs = append(dirs, filepath.Join(d, "applications"))
  139. }
  140. flatpakDirs := []string{filepath.Join(home, ".local/share/flatpak/exports/share/applications"),
  141. "/var/lib/flatpak/exports/share/applications"}
  142. for _, d := range flatpakDirs {
  143. if !slices.Contains(dirs, d) {
  144. dirs = append(dirs, d)
  145. }
  146. }
  147. return dirs
  148. }
  149. func tempDir() string {
  150. if os.Getenv("TMPDIR") != "" {
  151. return os.Getenv("TMPDIR")
  152. } else if os.Getenv("TEMP") != "" {
  153. return os.Getenv("TEMP")
  154. } else if os.Getenv("TMP") != "" {
  155. return os.Getenv("TMP")
  156. }
  157. return "/tmp"
  158. }
  159. func signalHandler() {
  160. signalChanel := make(chan os.Signal, 1)
  161. signal.Notify(signalChanel, syscall.SIGTERM, syscall.SIGUSR1)
  162. go func() {
  163. for {
  164. signalU := <-signalChanel
  165. switch signalU {
  166. case syscall.SIGTERM:
  167. fmt.Println("Exit... (SIGTERM)")
  168. gtk.MainQuit()
  169. case syscall.SIGUSR1:
  170. fmt.Println("Exit... (SIGUSR1)")
  171. gtk.MainQuit()
  172. default:
  173. fmt.Println("Unknow signal")
  174. }
  175. }
  176. }()
  177. }