web-dev-qa-db-ja.com

Linux FrameBufferを介してピクセルをスクリーンにペイントする

最近、/ dev/urandomから入力を取得し、関連する文字をランダムな整数に変換し、それらの整数をピクセルのrgb/x-y値として使用して画面にペイントするという奇妙なアイデアに驚きました。

私はいくつかの調査を行いました(ここではStackOverflowなど)。多くの人は、デバイスのファイル表現であるため、/ dev/fb0に直接書き込むことができることを示唆しています。残念ながら、これは視覚的に明らかな結果を生成しないようです。

バッファへの書き込みにmmapを使用したQTチュートリアル(もう利用できません)からのサンプルCプログラムを見つけました。プログラムは正常に実行されますが、画面には何も出力されません。おもしろいことに、ラップトップをサスペンドに入れて後で復元すると、フレームバッファーに書き込まれた画像の瞬間的なフラッシュ(赤い正方形)がかなり早く見えました。 Linuxで画面にペイントするためのフレームバッファへの書き込みはもう機能しますか?理想的には、(ba)shスクリプトを記述したいのですが、Cなども同様に機能します。ありがとう!

編集:これはサンプルプログラムです...獣医になじみがあるかもしれません。

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

int main()
{
    int fbfd = 0;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    long int screensize = 0;
    char *fbp = 0;
    int x = 0, y = 0;
    long int location = 0;

    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (fbfd == -1) {
        perror("Error: cannot open framebuffer device");
        exit(1);
    }
    printf("The framebuffer device was opened successfully.\n");

    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
        perror("Error reading fixed information");
        exit(2);
    }

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
        perror("Error reading variable information");
        exit(3);
    }

    printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

    // Map the device to memory
    fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
    if ((int)fbp == -1) {
        perror("Error: failed to map framebuffer device to memory");
        exit(4);
    }
    printf("The framebuffer device was mapped to memory successfully.\n");

    x = 100; y = 100;       // Where we are going to put the pixel

    // Figure out where in memory to put the pixel
    for (y = 100; y < 300; y++)
        for (x = 100; x < 300; x++) {

            location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
                       (y+vinfo.yoffset) * finfo.line_length;

            if (vinfo.bits_per_pixel == 32) {
                *(fbp + location) = 100;        // Some blue
                *(fbp + location + 1) = 15+(x-100)/2;     // A little green
                *(fbp + location + 2) = 200-(y-100)/5;    // A lot of red
                *(fbp + location + 3) = 0;      // No transparency
        //location += 4;
            } else  { //assume 16bpp
                int b = 10;
                int g = (x-100)/6;     // A little green
                int r = 31-(y-100)/16;    // A lot of red
                unsigned short int t = r<<11 | g << 5 | b;
                *((unsigned short int*)(fbp + location)) = t;
            }

        }
    munmap(fbp, screensize);
    close(fbfd);
    return 0;
}
34

X11を実行している場合は、X11 APIを使用して画面に描画する必要があります。 Xサーバーを迂回することは非常に壊れています(そして、これまで見てきたように、うまくいきません)。また、クラッシュや、一般的なディスプレイの破損を引き起こす可能性があります。

すべての場所(コンソールとXの両方)で実行できるようにする場合は、SDLまたはGGIを参照してください。 X11のみに関心がある場合は、GTK、QT、またはXlibを使用できます。たくさんのオプションがあります...

7
derobert

次のいくつかの実験で成功しました。

最初に、Xが32ビットに埋め込まれたTrueColor RGBを使用しているかどうかを調べます(または、これが当てはまると仮定します)。次に、fb0への書き込み許可があるかどうか(およびそれが存在するかどうか)を調べます。これらが当てはまる場合(そして、多くの最新のツールキット/デスクトップ/ PCがこれらをデフォルトとして使用するかもしれないと思う)、あなたは以下を行うことができるはずです(そしてこれらのデフォルトが保持されない場合、おそらくあなたはまだいくつかの成功を収めることができます詳細は異なる場合がありますが、次のテスト):

テスト1:仮想端末(X)を開き、次のように入力します。$ echo "ddd ... ddd">/dev/fb0ここで、...は実際にはdのいくつかの画面いっぱいです。結果は、エコー文字列の長さと有効にしたピクセル解像度に応じて、画面の上部に1つ以上の(部分的な)灰色の線が表示されます。任意の文字を選択することもできます(ASCII値はすべて0x80未満であるため、生成される色は濃い灰色になります。灰色以外の文字が必要な場合は文字を変更します)。明らかに、これはシェルループに一般化するか、大きなファイルをcatして効果をより明確に見ることができます。例:$ cat /lib/libc.so.6>/dev/fb0 fsfサポーター;-P

画面の大部分が上書きされても心配する必要はありません。 Xはまだマウスポインターを制御しており、ウィンドウがどこにマップされているかという考えを持っています。あなたがしなければならないのは、任意のウィンドウをつかみ、少しドラッグしてノイズを消すことです。

テスト2:cat/dev/fb0> xxxを実行し、デスクトップの外観を変更します(たとえば、新しいウィンドウを開いて他のウィンドウを閉じます)。最後に、逆にcat xxx>/dev/fb0を実行して、古いデスクトップを復元します!

ハ、まあ、そうでもない。古いデスクトップのイメージは幻想であり、ウィンドウをフルスクリーンで開くとすぐにそれがなくなります。

テスト3:/ dev/fb0の以前のダンプを取得し、ピクセルの色を変更する小さなアプリを作成します。たとえば、赤のコンポーネントを削除したり、青を増やしたり、赤と緑を反転したりします。テスト2の単純なシェルアプローチを使用して、後で見ることができる新しいファイルにピクセルを追加します。また、ピクセルごとにBGRA 4バイト量を処理する可能性が高いことに注意してください。これは、4バイトごとに無視し、各セットの最初のバイトを青のコンポーネントとして扱うことを意味します。 「ARGB」はビッグエンディアンです。したがって、C配列のインデックスを増やしてこれらのバイトにアクセスすると、最初に青、次に緑、赤の順になります。つまり、B-G-R-A(A-R-G-Bではありません).

テスト4:ウィンドウの境界なしでアニメーションを作成するために、画面の一部に非正方形の画像(xeyesと考えてください)を送信するビデオ速度でループする任意の言語でアプリを記述します。余分なポイントについては、アニメーションを画面全体に移動させます。小さい行に相当するピクセルを描画した後、大きなスペースをスキップすることを確認する必要があります(アニメーション化される画像よりもはるかに広い画面幅を補うため)。

テスト5:友達にトリックをかけます。たとえば、テスト4を拡張して、アニメーションの人物の写真がデスクトップにポップアップ表示されるようにし(ピクセルデータを取得するために自分で撮影する)、重要なデスクトップの1つに移動しますフォルダーを取り出してフォルダーを細断し、ヒステリックに笑い始め、火の玉が出てデスクトップ全体を飲み込みます。これはすべて幻想になりますが、彼らは少しおかしくなります。しかし、それを学習経験として使用して、Linuxとオープンソースを披露し、初心者よりも実際よりもはるかに怖いことを示します。 [Linuxでの「ウイルス」は一般に無害な幻想です]

11
Jose_X

上記のように、/ dev/fb0に書き込む前に注意してください。私はubuntu 10.04のXの下で試してみましたが、a)視覚的には何も起こりませんでした、b)すべてのシェルウィンドウ、他のttyも破壊し、カーネルエラーと機能不足につながりました。

2
David

乗算を自分で行うのではなく、screensizeにfb_fix_screeninfo.smem_lenを使用する必要があります。バッファーは、4バイトまたは他の何かに整列している場合があります。

screensize = finfo.smem_len;
1
Alex Arsenault

プログラムをデバッグすると、次の行が見つかります。

 screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

screensizeは0です。vinfo.xresは0であるため、次のように変更する必要があります。

long ppc_fx = (((long)fixed_info.smem_start) - ((long) fixed_info.smem_start & ~(PAGE_SIZE-1)));
screensize = finfo.smem_len + ppc_fx;

linux 2.6.2以降? 、mmap()の2番目の引数screensizeは0であってはなりません。そうでない場合、mmap()はMAP_FAILEDを返します。

0
Hsiang Chen

スクロールできるようにする必要があるという理由だけで、フレームバッファーベースのプログラムを書くことを考えています(SDRウォーターフォール)。 https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=232493&p=1425567#p1425567 スクロールテストが成功したと見なします。これらの2つの構造体では、ioctlを使用して情報を取得しますが、色深度に関するものもあります。あなたは私と同じ例に基づいてプログラムを作成したようです。 Linux(Raspberry Pi)でフレームバッファーからピクセルカラーを取得する方法

私のRaspberry Piでは、Xを使用してもしなくても、私のものは問題なく動作します。私のラップトップの画面には影響しません。/dev/fb0があり、プログラムは実行され、数字は正しく見えますが、視覚的には何もしません。多分それはダブルバッファリングか何かでしょう。

Xでは、実際にはダメージを与えません。いくつかのウィンドウをドラッグすると、すべてが再描画されます。それから、画面に表示されているものをバックアップし、完了したら元に戻すことにしました。移動して元に戻したウィンドウは、触れたことがない場合と同じように機能します。 Xは、スクリーンバッファーを台無しにしたことに気づきません。Xがそこに何を置いているかを認識し、それに応じてマウスクリックを登録します。ウィンドウを移動して元に戻さなかった場合、クリックは元の場所で機能します。

0
Alan Corey

このプログラムを使用して全画面を記述したときにクラッシュしたのは、画面サイズの計算が間違っているためです。

// Figure out the size of the screen in bytes     
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

これは次のとおりです。

/* Calculate the size of the screen in bytes */   
screensize = vinfo.xres_virtual * vinfo.yres_virtual * (vinfo.bits_per_pixel / 8);
0
Nataraja KM