+ set_buffer_internal (buf);
+
+ /* Handle centering on a graphical frame specially. Such frames can
+ have variable-height lines and centering point on the basis of
+ line counts would lead to strange effects. */
+ if (FRAME_WINDOW_P (XFRAME (w->frame)))
+ {
+ if (center_p)
+ {
+ struct it it;
+ struct text_pos pt;
+
+ SET_TEXT_POS (pt, PT, PT_BYTE);
+ start_display (&it, w, pt);
+ move_it_vertically (&it, - window_box_height (w) / 2);
+ charpos = IT_CHARPOS (it);
+ bytepos = IT_BYTEPOS (it);
+ }
+ else if (XINT (arg) < 0)
+ {
+ struct it it;
+ struct text_pos pt;
+ int y0, y1, h, nlines;
+
+ SET_TEXT_POS (pt, PT, PT_BYTE);
+ start_display (&it, w, pt);
+ y0 = it.current_y;
+
+ /* The amount of pixels we have to move back is the window
+ height minus what's displayed in the line containing PT,
+ and the lines below. */
+ nlines = - XINT (arg) - 1;
+ move_it_by_lines (&it, nlines, 1);
+
+ y1 = line_bottom_y (&it);
+
+ /* If we can't move down NLINES lines because we hit
+ the end of the buffer, count in some empty lines. */
+ if (it.vpos < nlines)
+ y1 += (nlines - it.vpos) * CANON_Y_UNIT (it.f);
+
+ h = window_box_height (w) - (y1 - y0);
+
+ start_display (&it, w, pt);
+ move_it_vertically (&it, - h);
+ charpos = IT_CHARPOS (it);
+ bytepos = IT_BYTEPOS (it);
+ }
+ else
+ {
+ struct position pos;
+ pos = *vmotion (PT, - XINT (arg), w);
+ charpos = pos.bufpos;
+ bytepos = pos.bytepos;
+ }
+ }
+ else
+ {
+ struct position pos;
+ int ht = window_internal_height (w);
+
+ if (center_p)
+ arg = make_number (ht / 2);
+ else if (XINT (arg) < 0)
+ arg = make_number (XINT (arg) + ht);
+
+ pos = *vmotion (PT, - XINT (arg), w);
+ charpos = pos.bufpos;
+ bytepos = pos.bytepos;
+ }
+
+ /* Set the new window start. */
+ set_marker_both (w->start, w->buffer, charpos, bytepos);
+ w->window_end_valid = Qnil;
+ w->force_start = Qt;
+ if (bytepos == BEGV_BYTE || FETCH_BYTE (bytepos - 1) == '\n')
+ w->start_at_line_beg = Qt;
+ else
+ w->start_at_line_beg = Qnil;
+
+ set_buffer_internal (obuf);
+ return Qnil;