Open Jsewill opened 9 years ago
Honestly, I don't know (but I'd like to). I've never used shm
before. If I were to investigate, I'd probably look for examples in C that use XCB. XGB should hopefully be a straight-forward translation from that.
Indeed, that was the first place I went looking. It seems I need to brush up on tmpfs/shm, especially as it relates to X11 in general. I'll continue looking into it and reply with any findings.
@Jsewill yes, please do! I'm personally quite carious. Even if you're findings are, "I found this good C example but I can't translate it to Go. Can you help?" :-)
Well, I was able to use shm.NewSegId(), shm.CreateSegment().Reply(), and shm.GetImage().Reply() without error. This gets me a segment ID and file descriptor (I think), and has X create the image in that segment. I'm not really sure how to read it.
In my research earlier in the week, I came across an example in C from the VLC project: https://github.com/videolan/vlc/blob/master/modules/access/screen/xcb.c . This is the best example I have found yet. Perhaps I need to attach and detach the segment to and from X and my program, though I was hoping this was happening automatically.
As far as I know, SHM related stuff was explicitly left out of Golang's builtin, "syscall". Short of using a Go wrapper for the C shm related functions, do you have any insight regarding all this?
I've made some progress, but now I'm hitting a wall. The X server comes back with "BadIDChoice" when attempting to attach an SHM segment, whether I create the segment using shm.CreateSegment() or wrapped C functions. Any ideas?
After revisiting this, I figured out the proper arguements and their order, and with that X stops complaining.
The current process is as such:
I still have a problem accessing the data from the segment yet, but I'll work through that when I have more time to work on it.
@Jsewill Can you post the code for this?
Here is working version, figured it out.
package main
/*
#include <sys/ipc.h>
#include <sys/shm.h>
*/
import "C"
import (
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/shm"
"github.com/BurntSushi/xgb/xproto"
"fmt"
"image"
"time"
"unsafe"
)
func main() {
scr := newXgbShmScreen()
for i := 0; i < 100; i++ {
start := time.Now()
scr.capture()
elapsed := time.Since(start)
fmt.Printf("%d\n", elapsed.Nanoseconds())
}
scr.Close()
}
type xgbShmScreen struct {
conn *xgb.Conn
screen *xproto.ScreenInfo
shmId int
data unsafe.Pointer
shmSize int
seg shm.Seg
}
func newXgbShmScreen() *xgbShmScreen {
var scr xgbShmScreen
var err error
scr.conn, err = xgb.NewConn()
if err != nil || shm.Init(scr.conn) != nil {
return nil
}
scr.screen = xproto.Setup(scr.conn).DefaultScreen(scr.conn)
scr.shmSize = int(scr.screen.WidthInPixels) * int(scr.screen.HeightInPixels) * 4
scr.shmId = int(C.shmget(C.IPC_PRIVATE, C.size_t(scr.shmSize), C.IPC_CREAT|0777))
if scr.shmId == -1 {
return nil
}
scr.data = C.shmat(C.int(scr.shmId), nil, 0)
scr.seg, err = shm.NewSegId(scr.conn)
if err != nil {
return nil
}
shm.AttachChecked(scr.conn, scr.seg, uint32(scr.shmId), false)
return &scr
}
func (scr *xgbShmScreen) capture() (imgData []byte, err error) {
_, err = shm.GetImage(scr.conn, xproto.Drawable(scr.screen.Root), 0, 0, scr.screen.WidthInPixels, scr.screen.HeightInPixels, 0xffffffff, byte(xproto.ImageFormatZPixmap), scr.seg, 0).Reply()
if err != nil {
return nil, err
}
imgData = (*[1 << 30]byte)(scr.data)[:scr.shmSize:scr.shmSize]
return imgData, nil
}
func (scr *xgbShmScreen) Close() {
shm.Detach(scr.conn, scr.seg)
C.shmctl(C.int(scr.shmId), C.IPC_RMID, nil)
C.shmdt(scr.data)
scr.conn.Close()
}
@BurntSushi
This is not a bug, just a lack of understanding on my part. I have put xproto.GetImage() to good use in a project I'm working on, but I'd like to use shm.GetImage() to get an image from the X server via SHM, where should I start? I apologize if it is inappropriate for me to post this as an issue, and I'd be glad to delete it if needed.