OSDN Git Service

[Feature] Xft/X11版でカーソルおよび選択領域の下の文字をわかるようにする
authorHabu <habu1010+github@gmail.com>
Tue, 20 Jun 2023 05:42:01 +0000 (14:42 +0900)
committerHabu <habu1010+github@gmail.com>
Wed, 21 Jun 2023 15:05:15 +0000 (00:05 +0900)
以前よりXftを有効にしたX11版でカーソルやドラッグした選択範囲の矩形領域が
塗りつぶされてしまい下の文字がわからなくなるという問題がある。
非常に悪いユーザ体験なので、カーソルは下の文字が反転表示されるように、
選択範囲は中の領域を塗りつぶさず枠だけの表示をするように修正する。

src/main-x11.cpp

index ab5b5c8..83ad10b 100644 (file)
 #include "util/int-char-converter.h"
 #include "util/string-processor.h"
 #include <algorithm>
+#include <memory>
+#include <span>
+#include <string>
 
 /*
  * Available graphic modes
 #include <X11/Xft/Xft.h>
 #endif
 
-#include <memory>
-#include <string>
-
 /*
  * Include some helpful X11 code.
  */
@@ -888,7 +888,7 @@ static void Infofnt_init_data(concptr name)
 }
 
 #ifdef USE_XFT
-static void Infofnt_text_std_xft_draw_str(int x, int y, concptr str, concptr str_end)
+static void Infofnt_text_std_xft_draw_str(int px, int py, const XftColor &fg, concptr str, concptr str_end)
 {
     int offset = 0;
     while (str < str_end) {
@@ -898,11 +898,25 @@ static void Infofnt_text_std_xft_draw_str(int x, int y, concptr str, concptr str
             return;
         }
 
-        XftDrawStringUtf8(Infowin->draw, &Infoclr->fg, Infofnt->info, x + Infofnt->wid * offset, y, (const FcChar8 *)str, byte_len);
+        XftDrawStringUtf8(Infowin->draw, &fg, Infofnt->info, px + Infofnt->wid * offset, py, (const FcChar8 *)str, byte_len);
         offset += (byte_len > 1 ? 2 : 1);
         str += byte_len;
     }
 }
+
+static void Infofnt_text_std_xft(int x, int y, int len, const XftColor &fg, const XftColor &bg, const char *str, int utf8_len)
+{
+    auto *draw = Infowin->draw;
+
+    const auto py = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
+    const auto px = (x * Infofnt->wid) + Infowin->ox;
+
+    XRectangle r{ 0, 0, static_cast<unsigned short>(Infofnt->wid * len), static_cast<unsigned short>(Infofnt->hgt) };
+    XftDrawSetClipRectangles(draw, px, py - Infofnt->asc, &r, 1);
+    XftDrawRect(draw, &bg, px, py - Infofnt->asc, r.width, r.height);
+    Infofnt_text_std_xft_draw_str(px, py, fg, str, str + utf8_len);
+    XftDrawSetClip(draw, 0);
+}
 #endif
 
 /*
@@ -918,8 +932,11 @@ static errr Infofnt_text_std(int x, int y, concptr str, int len)
         len = strlen(str);
     }
 
+#ifndef USE_XFT
     y = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
     x = (x * Infofnt->wid) + Infowin->ox;
+#endif
+
     if (Infofnt->mono) {
 #ifndef USE_XFT
         int i;
@@ -937,17 +954,7 @@ static errr Infofnt_text_std(int x, int y, concptr str, int len)
 #endif
 
 #ifdef USE_XFT
-        XftDraw *draw = Infowin->draw;
-
-        XRectangle r;
-        r.x = 0;
-        r.y = 0;
-        r.width = Infofnt->wid * len;
-        r.height = Infofnt->hgt;
-        XftDrawSetClipRectangles(draw, x, y - Infofnt->asc, &r, 1);
-        XftDrawRect(draw, &Infoclr->bg, x, y - Infofnt->asc, Infofnt->wid * len, Infofnt->hgt);
-        Infofnt_text_std_xft_draw_str(x, y, _(utf8_buf, str), _(utf8_buf + utf8_len, str + len));
-        XftDrawSetClip(draw, 0);
+        Infofnt_text_std_xft(x, y, len, Infoclr->fg, Infoclr->bg, _(utf8_buf, str), _(utf8_len, len));
 #else
         XmbDrawImageString(Metadpy->dpy, Infowin->win, Infofnt->info, Infoclr->gc, x, y, _(utf8_buf, str), _(utf8_len, len));
 #endif
@@ -1208,6 +1215,65 @@ static void sort_co_ord(co_ord *min, co_ord *max, const co_ord *b, const co_ord
     max->y = std::max(a->y, b->y);
 }
 
+#ifdef USE_XFT
+template <class T, class D>
+auto make_unique_ptr_with_deleter(T *p, D d) noexcept
+{
+    return std::unique_ptr<T, D>(p, std::move(d));
+}
+
+/*!
+ * @brief 矩形領域の枠を描画する
+ *
+ * ドラッグ時の選択範囲の表示に使用する。
+ *
+ * @param x 矩形領域の左上のX座標(ピクセル単位)
+ * @param y 矩形領域の左上のY座標(ピクセル単位)
+ * @param widht 矩形領域の幅(ピクセル単位)
+ * @param height 矩形領域の高さ(ピクセル単位)
+ */
+static void draw_rectangle_frame(int x, int y, int width, int height)
+{
+    auto gc = make_unique_ptr_with_deleter(XCreateGC(Metadpy->dpy, Infowin->win, 0, NULL),
+        [dpy = Metadpy->dpy](GC gc) { XFreeGC(dpy, gc); });
+
+    XSetForeground(Metadpy->dpy, gc.get(), WhitePixel(Metadpy->dpy, DefaultScreen(Metadpy->dpy)));
+    XDrawLine(Metadpy->dpy, Infowin->win, gc.get(), x, y, x + width, y);
+    XDrawLine(Metadpy->dpy, Infowin->win, gc.get(), x, y, x, y + height);
+    XDrawLine(Metadpy->dpy, Infowin->win, gc.get(), x + width, y, x + width, y + height);
+    XDrawLine(Metadpy->dpy, Infowin->win, gc.get(), x, y + height, x + width, y + height);
+}
+#endif
+
+#ifdef USE_XFT
+static void draw_cursor_xft(int x, int y, int len)
+{
+    // term_what() では中央寄せ時に座標がずれるので直接取得
+    const std::span<const char> cursor_chars(&game_term->scr->c[y][x], len);
+
+#ifdef JP
+    char utf8_buf[16];
+    const auto utf8_len = euc_to_utf8(cursor_chars.data(), cursor_chars.size(), utf8_buf, sizeof(utf8_buf));
+    if (utf8_len < 0) {
+        return;
+    }
+#endif
+    Infofnt_text_std_xft(x, y, len, Infoclr->bg, Infoclr->fg, _(utf8_buf, cursor_chars.data()), _(utf8_len, len));
+}
+#endif
+
+static void draw_cursor(int x, int y, int len)
+{
+#ifdef USE_XFT
+    draw_cursor_xft(x, y, len);
+#else
+    square_to_pixel(&x, &y, x, y);
+    const auto width = Infofnt->wid * len;
+    const auto height = Infofnt->hgt;
+    XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, width, height);
+#endif
+}
+
 /*
  * Remove the selection by redrawing it.
  */
@@ -1226,7 +1292,7 @@ static void mark_selection_mark(int x1, int y1, int x2, int y2)
     square_to_pixel(&x1, &y1, x1, y1);
     square_to_pixel(&x2, &y2, x2, y2);
 #ifdef USE_XFT
-    XftDrawRect(Infowin->draw, &clr[2]->fg, x1, y1, x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
+    draw_rectangle_frame(x1, y1, x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
 #else
     XDrawRectangle(Metadpy->dpy, Infowin->win, clr[2]->gc, x1, y1, x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
 #endif
@@ -1937,7 +2003,7 @@ static errr game_term_curs_x11(int x, int y)
 #endif
     } else {
         Infoclr_set(xor_.get());
-        Infofnt_text_non(x, y, " ", 1);
+        draw_cursor(x, y, 1);
     }
 
     return 0;
@@ -1960,7 +2026,7 @@ static errr game_term_bigcurs_x11(int x, int y)
 #endif
     } else {
         Infoclr_set(xor_.get());
-        Infofnt_text_non(x, y, "  ", 2);
+        draw_cursor(x, y, 2);
     }
 
     return 0;