3

I've written a little script in bash that opens a file and then five minutes later closes it. It repeats this procedure on and on.

The problem is that whereever the mouse is pointed the program opens, I would like it ot open on my second monitor which is the perfect size for viewing the file.

I have tried using the display variables in the following way (at the command line) export DISPLAY=:0.1. But then whatever program I call from the CLI after this return an error. (Error: cannot open display :0.1)

Does anyone have a suggestion?

edit:

Here is the script to open a file called thesis.pdf every five minutes

#! /bin/bash

while true; do
    evince /home/adam/Desktop/Thesis.pdf &
    sleep 5m
    ps -ef | grep "Thesis.pdf" | awk '{print $2}' | xargs kill
done

exit 0
10
  • Also set export XAUTHORITY=/home/user/.Xauthority along with export DISPLAY=:0.1 ..then check..
    – heemayl
    Jun 10, 2015 at 9:27
  • It still didn't work, gave the same error message (Error: cannot open display: :0.1)
    – Adam
    Jun 10, 2015 at 9:30
  • Are you sure the display name is correct ?
    – heemayl
    Jun 10, 2015 at 9:35
  • Not really sure, but it works for the first display when changed to :0.0
    – Adam
    Jun 10, 2015 at 9:38
  • Use xrandr -q or w to find the display names..
    – heemayl
    Jun 10, 2015 at 9:41

3 Answers 3

5

To position a window, you can use a tool that manipulates X events such as xdotool or wmctrl. For example, with wmctrl, you can use -e:

   -e <MVARG>
          Resize  and  move  a  window  that  has been specified with a -r
          action according to the <MVARG> argument.
   <MVARG>
          A move and resize argument has the format 'g,x,y,w,h'.  All five
          components are integers. The first value, g, is the  gravity  of
          the  window,  with  0  being  the most common value (the default
          value for the window). Please see  the  EWMH  specification  for
          other values.

          The four remaining values are a standard geometry specification:
          x,y is the position of the top left corner of  the  window,  and
          w,h  is  the  width and height of the window, with the exception
          that the value of -1 in any position is interpreted to mean that
          the current geometry value should not be modified.

You can usually ignore gravity, so to place a window at the top left corner of your screen and make it 1200 x 700 pixels, you would run:

wmctrl -r :ACTIVE: -e 1,1,1,1200,700

The -r lets you select a window and :ACTIVE: means the currently focused window.

You can also simplify your script. There is no reason to parse ps, the special variable $! holds the PID of the job most recently placed in the background. In any case, parsing ps will often fail since there might be multiple processes matching Thesis.pdf. There will always be two: the evince and the grep Thesis.pdf you just ran.

So, with all that in mind, you could do:

#! /bin/bash
while true; do
    ## Open the pdf
    evince ~/doc/a.pdf &
    ## Save the PID of evince
    pid="$!"
    ## Wait for a 1.5 seconds. This is to give the window time to
    ## appear. Change it to a higher value if your system is slower. 
    sleep 1.5
    ## Get the X name of the evince window
    name=$(wmctrl -lp | awk -vpid="$pid" '$3==pid{print $1}')
    ## Position the window
    wmctrl -ir "$name" -e 1,1,1,1200,700
    ## Wait
    sleep 5m
    ## Close it
    kill "$pid"
done

Note that I removed the exit 0 since, because of your while true, it will never be reached and there's no point to it. You can play with the positional arguments to figure out where you want to place the window.

Finally, a note on DISPLAY. This variables points to an X display. This is not a screen, it is the active X server. Many users might be running parallel X servers on a single machine, this allows you to choose which one of them a window should be displayed on. It has absolutely nothing to do with how many physical screens are connected, unless each screen is running a separate X session.

4
  • That's the way I'd do it. +1 ! Jun 10, 2015 at 14:31
  • Worked flawlessly, I just need to tweak the window sizes.
    – Adam
    Jun 15, 2015 at 10:25
  • Would this work on Wayland?
    – Flimm
    Jan 29, 2023 at 14:49
  • @Flimm no, I don't think so. As far as I know, there is no equivalent to wmctrl for Wayland.
    – terdon
    Jan 29, 2023 at 15:00
1

This answer assumes you are using Unity:

What you will run into when writing a script to open an application on a specific sreen or positon

If you want to call an application and, subsequently, place its window on a specific position and size, the time between calling the application and the moment the window actually appears, is essential, but unpredictable; if your system is occupied, it can be significantly longer than if it is idle.

You need a (preferably "smart") way to make sure the positioning/resizing is done (immediately) after the window appears.

The script below (an edited version of this one) opens an application, possibly with the file as argument, on any place on your desktop (on the second monitor as well). It waits for the window to appear before it moves the window to its position and size, so it will run correctly, no matter if your system is slow (or occupied) or not.

The script

#!/usr/bin/env python3
import subprocess
import time
import sys

app = (" ").join(sys.argv[5:])

get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
subprocess.Popen(["/bin/bash", "-c", app])

while t < 30:      
    ws2 = [w.split()[0:3] for w in get("wmctrl -lp").splitlines() if not w in ws1]
    procs = [[(p, w[0]) for p in get("ps -e ww").splitlines() \
              if app in p and w[2] in p] for w in ws2]
    if len(procs) > 0:
        w_id = procs[0][0][1]
        cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
        cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
        cmd3 = "xdotool windowsize --sync "+procs[0][0][1]+" "+sys.argv[3]+"% "+sys.argv[4]+"%"
        cmd4 = "xdotool windowmove "+procs[0][0][1]+" "+sys.argv[1]+" "+sys.argv[2]
        for cmd in [cmd1, cmd2, cmd3, cmd4]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1


How to use it

  1. install both xdotool and wmctrl. I used both since resizing with wmctrl can cause some peculiarities on (specifically) Unity.

    sudo apt-get install wmctrl
    sudo apt-get install xdotool
    
  2. Copy the script below into an empty file, save it as setwindow (no extension) in ~/bin; create the directory if necessary.

  3. Make the script executable (!)
  4. If you just created ~bin, run: source ~/.profile
  5. Test-run the script with the command like (e.g.)

    setwindow 0 0 50 100 gedit /path/to/file.txt
    

    In other words:

    setwindow <horizontal-position> <vertical-position> <horizontal-size (%)> <vertical-size (%)> <application> <file_to_open>
    

If all works fine, use the command wherever you need it.

To open the application on the monitor on the right

If you have a second monitor connected, you should think of the combined screens as one big virtual desktop, like the example below.

enter image description here

In this example, to place the window on the right screen, you simply need to make its x-position greater then the x-resolution of the left screen. (> 1680 in this case).

To open the file with your application in a maximized way, set both <horizontal-size (%)> and <vertical-size (%)> to 100.

Minor issue

In Unity, when you (re-)position and (re-)size a window with either wmctrl or xdotool, the window will always keep a small marge to the borders of your screen, unless you set it to 100%. You can see that in the image (3) above; while the inkscape window was placed on x position 0, you can still see a minor marge between the Unity Launcher and the inkscape window.

0

I'm taking an absolute shot-in-the-dark, here, but a workaround for now could be this script:

#!/bin/bash
# Author: Andrew Martin
# Credit: http://ubuntuforums.org/showthread.php?t=1309247
echo "Enter the primary display from the following:"            # prompt for the display
xrandr --prop | grep "[^dis]connected" | cut --delimiter=" " -f1    # query connected monitors
read choice                             # read the users's choice of monitor
xrandr --output $choice --primary                   # set the primary monitor

I believe this worked, for me, to set my middle monitor instead of my laptop monitor to the "main monitor." This, in theory, will open your program in the main monitor?-- I know you said it opens where you mouse is, but, it's a li'l shot...

Hope you solve it :)

1
  • Thanks, I was hoping it would work, but unfortunately it didn't. I ran the commands in the terminal, and programs still follow the mouse...
    – Adam
    Jun 10, 2015 at 11:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .